sql >> Database >  >> RDS >> Mysql

Prestaties van zoekopdracht op geïndexeerde Booleaanse kolom versus kolom Datum/tijd

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 van rand(1)<0.5 (50% verwijderd)
  • Tabelgrootte gewijzigd van rijen van 10 miljoen in 1 miljoen rijen.
  • SELECT COUNT(*) gewijzigd in SELECT *

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.



  1. Invoegen na afkappen start vanaf 1; maar invoegen na verwijderen hervat vanaf vorige waarde

  2. Hoe de SQL-uitvoeringstijd te beperken?

  3. Sqlalchemy, onbewerkte query en parameters

  4. SQLite GLOB