Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Leistung der Abfrage für indizierte boolesche Spalten im Vergleich zur Datetime-Spalte

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 von rand(1)<0.5 (50 % gelöscht)
  • Tabellengröße von 10 Mio. auf 1 Mio. Zeilen geändert.
  • SELECT COUNT(*) geändert in SELECT *

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.