如果在datetime
类型列上设置索引而不是boolean
类型列(并且在该列上进行查询),查询性能是否存在显着差异?
在我目前的设计中,我有两列:
is_active
TINYINT(1),已编入索引 deleted_at
DATETIME 查询为SELECT * FROM table WHERE is_active = 1;
如果我在deleted_at
列上创建了一个索引,并且运行了这样的SELECT * FROM table WHERE deleted_at is null;
这样的查询,会不会更慢?
答案 0 :(得分:8)
这是一个包含10M行的MariaDB(10.0.19)基准测试(使用sequence plugin):
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毫秒
TIMESTAMP 索引:
SELECT COUNT(*) FROM test WHERE deleted_at is null;
运行时间:〜 748毫秒
索引大小:
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字节)是TYNYINT(1字节)的4倍,但索引大小甚至不是两倍大。但如果索引大小不适合内存,则索引大小可能很大。因此,当我将innodb_buffer_pool_size
从1G
更改为50M
时,我会得到以下数字:
为了更直接地解决这个问题,我对数据进行了一些更改:
rand(1)<0.99
(已删除1%)而不是rand(1)<0.5
(已删除50%)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毫秒内执行。并删除is_active
列,deleted_at is null
查询在280毫秒内执行。
请注意,这仍然不是一个现实的情况。您不太可能希望从1M中选择990K行并将其交付给用户。您可能还会在表格中包含更多列(可能包括文本)。但它表明,您可能不需要is_active
列(如果它没有添加其他信息),并且任何索引在最佳情况下都无法用于选择未删除的条目。
但是,索引可用于选择已删除的行:
SELECT * FROM test WHERE is_active = 0;
使用索引执行10毫秒,不使用索引执行170毫秒。
SELECT * FROM test WHERE deleted_at is not null;
使用索引执行11毫秒,不使用索引执行167毫秒。
使用索引删除它在4毫秒内执行的is_active
列,在没有索引的情况下丢弃150毫秒。
因此,如果此方案以某种方式适合您的数据,结论将是:删除is_active
列,如果您很少选择已删除的条目,则不要在deleted_at
列上创建索引。或者根据您的需求调整基准并做出自己的结论。
答案 1 :(得分:0)
我认为is_active
会更快,但你可以测试一百万行。