索引布尔列与Datetime列的查询性能

时间:2017-03-18 13:52:23

标签: mysql sql performance indexing mariadb

如果在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;这样的查询,会不会更慢?

2 个答案:

答案 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_size1G更改为50M时,我会得到以下数字:

  • TINYINT:〜 960毫秒
  • TIMESTAMP:〜 1500毫秒

更新

为了更直接地解决这个问题,我对数据进行了一些更改:

  • 我使用DATETIME
  • 代替TIMESTAMP
  • 由于条目通常很少被删除,我使用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毫秒内执行。并删除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会更快,但你可以测试一百万行。