Ето бенчмарк на MariaDB (10.0.19) с 10 милиона реда (с помощта на приставката за последователност ):
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;
За измерване на времето използвам set profiling=1
и стартирайте show profile
след изпълнение на заявка. От резултата от профилирането вземам стойността на Sending data
тъй като всичко останало е по-малко от една мс.
TINYINT индекс:
SELECT COUNT(*) FROM test WHERE is_active = 1;
Време на работа:~ 738 msec
TIMESTAMP индекс:
SELECT COUNT(*) FROM test WHERE deleted_at is null;
Време на изпълнение:~ 748 msec
Размер на индекса:
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'
Резултат:
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
Имайте предвид, че докато TIMESTAMP (4 байта) е 4 пъти по-дълъг от TYNYINT (1 байт), размерът на индекса не е дори два пъти по-голям. Но размерът на индекса може да бъде значителен, ако не се вписва в паметта. Така че, когато променя innodb_buffer_pool_size
от 1G
до 50M
получавам следните числа:
- TINYINT:~ 960 msec
- TIMESTAMP:~ 1500 msec
Актуализиране
За да отговоря на въпроса по-директно, направих някои промени в данните:
- Вместо TIMESTAMP използвам DATETIME
- Тъй като записите обикновено се изтриват рядко, използвам
rand(1)<0.99
(1% изтрит) вместоrand(1)<0.5
(50% изтрити) - Размерът на таблицата е променен от 10M на 1M реда.
SELECT COUNT(*)
променен наSELECT *
Размер на индекса:
index_name | stat_value*@@innodb_page_size
------------------------------------------
PRIMARY | 25739264
deleted_at | 12075008
is_active | 11026432
От 99% от deleted_at
стойностите са NULL, няма значителна разлика в размера на индекса, въпреки че непразен DATETIME изисква 8 байта (MariaDB).
SELECT * FROM test WHERE is_active = 1; -- 782 msec
SELECT * FROM test WHERE deleted_at is null; -- 829 msec
Отпадането на двата индекса и двете заявки се изпълняват за около 350 msec. И премахване на is_active
колоната deleted_at is null
заявката се изпълнява за 280 msec.
Имайте предвид, че това все още не е реалистичен сценарий. Малко вероятно ще искате да изберете 990K реда от 1M и да ги доставите на потребителя. Вероятно ще имате и повече колони (може би включително текст) в таблицата. Но това показва, че вероятно нямате нужда от is_active
колона (ако не добавя допълнителна информация) и че всеки индекс в най-добрия случай е безполезен за избор на неизтрити записи.
Индексът обаче може да бъде полезен за избор на изтрити редове:
SELECT * FROM test WHERE is_active = 0;
Изпълнява се за 10 msec с индекс и за 170 msec без индекс.
SELECT * FROM test WHERE deleted_at is not null;
Изпълнява се за 11 msec с индекс и за 167 msec без индекс.
Отпадане на is_active
колона се изпълнява за 4 мсек с индекс и за 150 мсек без индекс.
Така че, ако този сценарий по някакъв начин отговаря на вашите данни, заключението би било:Изхвърлете is_active
колона и не създавайте индекс на deleted_at
колона, ако рядко избирате изтрити записи. Или коригирайте референтния показател според вашите нужди и направете свое собствено заключение.