Hier is een MariaDB (10.0.19) benchmark met 10 miljoen rijen (met behulp van de sequentie-plug-in ):
drop table if exists test;
CREATE TABLE `test` (
`id` MEDIUMINT UNSIGNED NOT NULL,
`is_active` TINYINT UNSIGNED NOT NULL,
`deleted_at` TIMESTAMP NULL,
PRIMARY KEY (`id`),
INDEX `is_active` (`is_active`),
INDEX `deleted_at` (`deleted_at`)
) ENGINE=InnoDB
select seq id
, rand(1)<0.5 as is_active
, case when rand(1)<0.5
then null
else '2017-03-18' - interval floor(rand(2)*1000000) second
end as deleted_at
from seq_1_to_10000000;
Om de tijd te meten gebruik ik set profiling=1
en voer show profile
uit na het uitvoeren van een query. Uit het profileringsresultaat neem ik de waarde van Sending data
aangezien al het andere in totaal minder dan één msec is.
TINYINT index:
SELECT COUNT(*) FROM test WHERE is_active = 1;
Runtime:~ 738 msec
TIMESTAMP index:
SELECT COUNT(*) FROM test WHERE deleted_at is null;
Runtime:~ 748 msec
Indexgrootte:
select database_name, table_name, index_name, stat_value*@@innodb_page_size
from mysql.innodb_index_stats
where database_name = 'tmp'
and table_name = 'test'
and stat_name = 'size'
Resultaat:
database_name | table_name | index_name | stat_value*@@innodb_page_size
-----------------------------------------------------------------------
tmp | test | PRIMARY | 275513344
tmp | test | deleted_at | 170639360
tmp | test | is_active | 97107968
Merk op dat hoewel TIMESTAMP (4 Bytes) 4 keer zo lang is als TYNYINT (1 Byte), de index niet eens twee keer zo groot is. Maar de indexgrootte kan aanzienlijk zijn als deze niet in het geheugen past. Dus als ik innodb_buffer_pool_size
verander van 1G
tot 50M
ik krijg de volgende nummers:
- TINYINT:~ 960 msec
- TIMESTAMP:~ 1500 msec
Bijwerken
Om de vraag directer te beantwoorden, heb ik enkele wijzigingen in de gegevens aangebracht:
- In plaats van TIMESTAMP gebruik ik DATETIME
- Omdat items meestal zelden worden verwijderd, gebruik ik
rand(1)<0.99
(1% verwijderd) in plaats vanrand(1)<0.5
(50% verwijderd) - Tabelgrootte gewijzigd van rijen van 10 miljoen in 1 miljoen rijen.
SELECT COUNT(*)
gewijzigd inSELECT *
Indexgrootte:
index_name | stat_value*@@innodb_page_size
------------------------------------------
PRIMARY | 25739264
deleted_at | 12075008
is_active | 11026432
Sinds 99% van deleted_at
waarden zijn NULL, er is geen significant verschil in indexgrootte, hoewel een niet-lege DATETIME 8 Bytes (MariaDB) vereist.
SELECT * FROM test WHERE is_active = 1; -- 782 msec
SELECT * FROM test WHERE deleted_at is null; -- 829 msec
Als u beide indexen laat vallen, worden beide query's in ongeveer 350 msec uitgevoerd. En het verwijderen van de is_active
kolom de deleted_at is null
query wordt uitgevoerd in 280 msec.
Merk op dat dit nog steeds geen realistisch scenario is. Het is onwaarschijnlijk dat u 990K rijen uit 1M wilt selecteren en deze aan de gebruiker wilt leveren. U zult waarschijnlijk ook meer kolommen (misschien inclusief tekst) in de tabel hebben. Maar het laat zien dat je waarschijnlijk de is_active
. niet nodig hebt kolom (als het geen aanvullende informatie toevoegt), en dat een index in het beste geval nutteloos is voor het selecteren van niet-verwijderde items.
Een index kan echter handig zijn om verwijderde rijen te selecteren:
SELECT * FROM test WHERE is_active = 0;
Wordt uitgevoerd in 10 msec met index en in 170 msec zonder index.
SELECT * FROM test WHERE deleted_at is not null;
Wordt uitgevoerd in 11 msec met index en in 167 msec zonder index.
Het verwijderen van de is_active
kolom wordt het uitgevoerd in 4 msec met index en in 150 msec zonder index.
Dus als dit scenario op de een of andere manier bij uw gegevens past, zou de conclusie zijn:Laat de is_active
vallen kolom en maak geen index op deleted_at
kolom als u zelden verwijderde items selecteert. Of pas de benchmark aan uw behoeften aan en trek uw eigen conclusie.