Hier ist ein MariaDB-Benchmark (10.0.19) mit 10 Millionen Zeilen (unter Verwendung des sequence plugin ):
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;
Um die Zeit zu messen verwende ich set profiling=1
und führen Sie show profile
aus nach Ausführung einer Abfrage. Aus dem Profiling-Ergebnis entnehme ich den Wert von Sending data
da alles andere insgesamt weniger als eine ms ist.
TINYINT index:
SELECT COUNT(*) FROM test WHERE is_active = 1;
Laufzeit:~ 738 ms
ZEITSTEMPEL index:
SELECT COUNT(*) FROM test WHERE deleted_at is null;
Laufzeit:~ 748 ms
Indexgröße:
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'
Ergebnis:
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
Beachten Sie, dass TIMESTAMP (4 Byte) zwar viermal so lang ist wie TYNYINT (1 Byte), die Indexgröße jedoch nicht einmal doppelt so groß ist. Die Indexgröße kann jedoch erheblich sein, wenn sie nicht in den Speicher passt. Wenn ich also innodb_buffer_pool_size
ändere von 1G
auf 50M
Ich bekomme die folgenden Nummern:
- TINYINT:~ 960 ms
- ZEITSTEMPEL:~ 1500 ms
Aktualisieren
Um die Frage direkter anzugehen, habe ich einige Änderungen an den Daten vorgenommen:
- Statt TIMESTAMP verwende ich DATETIME
- Da Einträge normalerweise selten gelöscht werden verwende ich
rand(1)<0.99
(1 % gelöscht) anstelle vonrand(1)<0.5
(50 % gelöscht) - Tabellengröße von 10 Mio. auf 1 Mio. Zeilen geändert.
SELECT COUNT(*)
geändert inSELECT *
Indexgröße:
index_name | stat_value*@@innodb_page_size
------------------------------------------
PRIMARY | 25739264
deleted_at | 12075008
is_active | 11026432
Da 99 % von deleted_at
Werte NULL sind, gibt es keinen signifikanten Unterschied in der Indexgröße, obwohl eine nicht leere DATETIME 8 Bytes benötigt (MariaDB).
SELECT * FROM test WHERE is_active = 1; -- 782 msec
SELECT * FROM test WHERE deleted_at is null; -- 829 msec
Wenn beide Indizes gelöscht werden, werden beide Abfragen in etwa 350 ms ausgeführt. Und das Löschen von is_active
Spalte deleted_at is null
Abfrage wird in 280 ms ausgeführt.
Beachten Sie, dass dies immer noch kein realistisches Szenario ist. Es ist unwahrscheinlich, dass Sie 990.000 Zeilen aus 1 Million auswählen und an den Benutzer liefern möchten. Sie werden wahrscheinlich auch mehr Spalten (vielleicht einschließlich Text) in der Tabelle haben. Aber es zeigt, dass Sie den is_active
wahrscheinlich nicht brauchen Spalte (wenn sie keine zusätzlichen Informationen hinzufügt) und dass jeder Index im besten Fall nutzlos ist, um nicht gelöschte Einträge auszuwählen.
Ein Index kann jedoch nützlich sein, um gelöschte Zeilen auszuwählen:
SELECT * FROM test WHERE is_active = 0;
Ausführung in 10 ms mit Index und in 170 ms ohne Index.
SELECT * FROM test WHERE deleted_at is not null;
Ausführung in 11 ms mit Index und in 167 ms ohne Index.
Das Löschen des is_active
Spalte wird es in 4 ms mit Index und in 150 ms ohne Index ausgeführt.
Wenn dieses Szenario also irgendwie zu Ihren Daten passt, wäre die Schlussfolgerung:Lassen Sie den is_active
fallen -Spalte und erstellen Sie keinen Index auf deleted_at
Spalte, wenn Sie selten gelöschte Einträge auswählen. Oder passen Sie den Benchmark an Ihre Bedürfnisse an und ziehen Sie Ihr eigenes Fazit.