sql >> Databáze >  >> RDS >> Mysql

Výkon dotazu na indexovaný booleovský sloupec vs sloupec Datetime

Zde je srovnávací test MariaDB (10.0.19) s 10 miliony řádků (pomocí sekvenčního pluginu ):

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;

K měření času používám set profiling=1 a spusťte show profile po provedení dotazu. Z výsledku profilování beru hodnotu Sending data protože vše ostatní je celkem méně než jedna ms.

TINYINT index:

SELECT COUNT(*) FROM test WHERE is_active = 1;

Doba běhu:~ 738 ms

TIMESTAMP index:

SELECT COUNT(*) FROM test WHERE  deleted_at is null;

Doba běhu:~ 748 ms

Velikost indexu:

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'

Výsledek:

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 

Všimněte si, že zatímco TIMESTAMP (4 bajty) je 4krát delší než TYNYINT (1 bajt), velikost indexu není ani dvakrát tak velká. Ale velikost indexu může být významná, pokud se nevejde do paměti. Takže když změním innodb_buffer_pool_size z 1G50M dostanu následující čísla:

  • TINYINT:~ 960 ms
  • TIMESTAMP:~ 1500 ms

Aktualizovat

Abych odpověděl na otázku příměji, provedl jsem několik změn v datech:

  • Namísto TIMESTAMP používám DATETIME
  • Vzhledem k tomu, že záznamy se obvykle mažou jen zřídka, používám rand(1)<0.99 (1 % smazáno) místo rand(1)<0.5 (50 % smazáno)
  • Velikost tabulky se změnila z 10 milionů na 1 milion řádků.
  • SELECT COUNT(*) změněno na SELECT *

Velikost indexu:

index_name | stat_value*@@innodb_page_size
------------------------------------------
PRIMARY    | 25739264
deleted_at | 12075008
is_active  | 11026432

Protože 99 % deleted_at hodnoty jsou NULL, neexistuje žádný významný rozdíl ve velikosti indexu, i když neprázdné DATETIME vyžaduje 8 bajtů (MariaDB).

SELECT * FROM test WHERE is_active = 1;      -- 782 msec
SELECT * FROM test WHERE deleted_at is null; -- 829 msec

Po zrušení obou indexů se oba dotazy provedou za přibližně 350 ms. A zrušením is_active sloupec deleted_at is null dotaz se provede za 280 ms.

Všimněte si, že to stále není realistický scénář. Pravděpodobně nebudete chtít vybrat 990 tisíc řádků z 1 milionu a doručit je uživateli. Pravděpodobně budete mít v tabulce také více sloupců (třeba včetně textu). Ale ukazuje to, že pravděpodobně nepotřebujete is_active (pokud nepřidává další informace) a že jakýkoli index je v nejlepším případě pro výběr nesmazaných položek k ničemu.

Pro výběr odstraněných řádků však může být užitečný index:

SELECT * FROM test WHERE is_active = 0;

Spustí se za 10 ms s indexem a za 170 ms bez indexu.

SELECT * FROM test WHERE deleted_at is not null;

Spustí se za 11 ms s indexem a za 167 ms bez indexu.

Vypuštěním is_active sloupec se provede za 4 ms s indexem a za 150 ms bez indexu.

Takže pokud tento scénář nějak odpovídá vašim datům, závěr by byl:Zrušte is_active a nevytvářejte index na deleted_at Pokud zřídka vybíráte smazané položky. Nebo upravte měřítko podle svých potřeb a udělejte si vlastní závěr.



  1. Jak připojit databázi mySQL pomocí C++

  2. Proč se M objevuje ve výsledcích dotazů Clojure MySQL

  3. Opakujte řetězec vícekrát v MySQL – REPEAT()

  4. Výběr procesoru pro SQL Server 2014 – část 1