Här är ett MariaDB (10.0.19) benchmark med 10 miljoner rader (med sekvensplugin a> ):
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;
För att mäta tiden använder jag set profiling=1
och kör show profile
efter att ha kört en fråga. Från profileringsresultatet tar jag värdet Sending data
eftersom allt annat är totalt mindre än en msek.
TINYINT index:
SELECT COUNT(*) FROM test WHERE is_active = 1;
Körtid:~ 738 msek
TIDSSTÄMPEL index:
SELECT COUNT(*) FROM test WHERE deleted_at is null;
Körtid:~ 748 msek
Indexstorlek:
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'
Resultat:
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
Observera att även om TIMESTAMP (4 byte) är 4 gånger så lång som TYNYINT (1 byte), är indexstorleken inte ens dubbelt så stor. Men indexstorleken kan vara betydande om den inte passar in i minnet. Så när jag ändrar innodb_buffer_pool_size
från 1G
till 50M
jag får följande siffror:
- TINYINT:~ 960 msek
- TIDSSTÄMPEL:~ 1500 msek
Uppdatera
För att ta upp frågan mer direkt gjorde jag några ändringar i uppgifterna:
- Istället för TIMESTAMP använder jag DATETIME
- Eftersom poster vanligtvis sällan raderas använder jag
rand(1)<0.99
(1 % raderad) istället förrand(1)<0.5
(50 % raderade) - Tabellstorleken har ändrats från 10 miljoner till 1 miljoner rader.
SELECT COUNT(*)
ändrat tillSELECT *
Indexstorlek:
index_name | stat_value*@@innodb_page_size
------------------------------------------
PRIMARY | 25739264
deleted_at | 12075008
is_active | 11026432
Sedan 99 % av deleted_at
värdena är NULL det finns ingen signifikant skillnad i indexstorlek, även om en icke tom DATETIME kräver 8 byte (MariaDB).
SELECT * FROM test WHERE is_active = 1; -- 782 msec
SELECT * FROM test WHERE deleted_at is null; -- 829 msec
Om båda indexen släpps körs båda frågorna på cirka 350 msek. Och släpper is_active
kolumnen deleted_at is null
fråga körs på 280 msek.
Observera att detta fortfarande inte är ett realistiskt scenario. Det är osannolikt att du vill välja 990 000 rader av 1 miljoner och leverera det till användaren. Du kommer förmodligen också att ha fler kolumner (kanske inklusive text) i tabellen. Men det visar att du förmodligen inte behöver is_active
kolumn (om den inte lägger till ytterligare information), och att något index i bästa fall är värdelöst för att välja icke raderade poster.
Ett index kan dock vara användbart för att välja borttagna rader:
SELECT * FROM test WHERE is_active = 0;
Körs på 10 msek med index och på 170 msek utan index.
SELECT * FROM test WHERE deleted_at is not null;
Körs på 11 msek med index och på 167 msek utan index.
Släpp is_active
kolumn den körs på 4 msek med index och i 150 msek utan index.
Så om detta scenario på något sätt stämmer överens med dina data skulle slutsatsen bli:Släpp is_active
kolumn och skapa inte ett index på deleted_at
kolumnen om du sällan väljer borttagna poster. Eller justera riktmärket efter dina behov och gör din egen slutsats.