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 1G
až 50M
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ístorand(1)<0.5
(50 % smazáno) - Velikost tabulky se změnila z 10 milionů na 1 milion řádků.
SELECT COUNT(*)
změněno naSELECT *
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.