时间戳列上的 MySQL 索引不用于大日期范围

Posted

技术标签:

【中文标题】时间戳列上的 MySQL 索引不用于大日期范围【英文标题】:MySQL index on timestamp column not used for large date ranges 【发布时间】:2013-08-20 06:55:04 【问题描述】:

我有桌子

+-------------------+----------------+------+-----+---------------------+-----------------------------+
| Field             | Type           | Null | Key | Default             | Extra                       |
+-------------------+----------------+------+-----+---------------------+-----------------------------+
| id                | bigint(20)     | NO   | PRI | NULL                | auto_increment              |
| runtime_id        | bigint(20)     | NO   | MUL | NULL                |                             |
| place_id          | bigint(20)     | NO   | MUL | NULL                |                             |
| amended_timestamp | varchar(50)    | YES  |     | NULL                |                             |
| applicable_at     | timestamp      | NO   |     | CURRENT_TIMESTAMP   | on update CURRENT_TIMESTAMP |
| schedule_time     | timestamp      | NO   | MUL | 0000-00-00 00:00:00 |                             |
| quality_indicator | varchar(10)    | NO   |     | NULL                |                             |
| flow_rate         | decimal(15,10) | NO   |     | NULL                |                             |
+-------------------+----------------+------+-----+---------------------+-----------------------------+

我在 schedule_time 上有索引

create index table_index on table(schedule_time asc);

该表目前有 2121552+ 条记录。

我不明白的是当我解释时

explain select runtime_id from table where schedule_time >= now() - INTERVAL 1 DAY;
+----+-------------+----------+-------+------------------------------+------------------------------+---------+------+-------+-------------+
| id | select_type | table    | type  | possible_keys                | key                          | key_len | ref  | rows  | Extra       |
+----+-------------+----------+-------+------------------------------+------------------------------+---------+------+-------+-------------+
|  1 | SIMPLE      | table    | range | table_index                  | table_index                  | 4       | NULL | 38088 | Using where |
+----+-------------+----------+-------+------------------------------+------------------------------+---------+------+-------+-------------+
1 row in set (0.00 sec)

使用上面的索引,但不使用下面的。

mysql> explain select runtime_id from table where schedule_time >= now() - INTERVAL 30 DAY;
+----+-------------+----------+------+------------------------------+------+---------+------+---------+-------------+
| id | select_type | table    | type | possible_keys                | key  | key_len | ref  | rows    | Extra       |
+----+-------------+----------+------+------------------------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | table    | ALL  | table_index                  | NULL | NULL    | NULL | 2118107 | Using where |
+----+-------------+----------+------+------------------------------+------+---------+------+---------+-------------+
1 row in set (0.00 sec)

如果有人能指出这里有什么问题,我将不胜感激,因为数据每 12 分钟更新一次,并且随着时间的流逝,查询 30 天或可能是 60 天会变得非常慢。

我打算使用它的最终查询如下

select avg(flow_rate),c.group from table a ,(select runtime_id from table where schedule_time >= now() - INTERVAL 1 DAY group by schedule_time ) b,place c  where a.runtime_id = b.runtime_id and a.place_id = c.id group by c.group;

更新 =====>

根据 cmets 之间的失败也失败了。

mysql> explain select runtime_id from table where schedule_time between '2013-07-17 12:48:00' and '2013-08-17 12:48:00';
+----+-------------+----------+------+------------------------------+------+---------+------+---------+-------------+
| id | select_type | table    | type | possible_keys                | key  | key_len | ref  | rows    | Extra       |
+----+-------------+----------+------+------------------------------+------+---------+------+---------+-------------+
|  1 | SIMPLE      | table    | ALL  | table_index                  | NULL | NULL    | NULL | 2118431 | Using where |
+----+-------------+----------+------+------------------------------+------+---------+------+---------+-------------+
1 row in set (0.00 sec)

mysql> explain select runtime_id from table where schedule_time between '2013-08-16 12:48:00' and '2013-08-17 12:48:00';
+----+-------------+----------+-------+------------------------------+------------------------------+---------+------+-------+-------------+
| id | select_type | table    | type  | possible_keys                | key                          | key_len | ref  | rows  | Extra       |
+----+-------------+----------+-------+------------------------------+------------------------------+---------+------+-------+-------------+
|  1 | SIMPLE      | table    | range | table_index                  | table_index                  | 4       | NULL | 38770 | Using where |
+----+-------------+----------+-------+------------------------------+------------------------------+---------+------+-------+-------------+
1 row in set (0.00 sec)

更新 2 =======>

mysql> select count(*) from table where schedule_time between '2013-08-16 12:48:00' and '2013-08-17 12:48:00';
+----------+
| count(*) |
+----------+
|    19440 |
+----------+
1 row in set (0.01 sec)

mysql> select count(*) from table where schedule_time between '2013-07-17 12:48:00' and '2013-08-17 12:48:00';
+----------+
| count(*) |
+----------+
|   597132 |
+----------+
1 row in set (0.00 sec)

服务器版本:5.5.24-0ubuntu0.12.04.1 (Ubuntu)

【问题讨论】:

这里有类似的问题 - 想法是使用'between'语句:***.com/questions/2041575/… 我无法重现这种行为(没有那么努力,我想......)只是一个疯狂的猜测:运行 ANALYZE TABLE my_table 会改善情况吗? @SylvainLeroux 我运行了 ANALYZE TABLE 但结果与 between 和 Interval 相同 我找到了这个链接bugs.mysql.com/bug.php?id=64998我猜mysql 5.5.24版本有bug 600 k / 2100 k = 整个表的 28%,我认为优化器可能会做出一个很好的决定,优先对索引进行全表扫描,对整个表进行顺序扫描可能更快与在这种情况下使用索引进行随机访问相比,您可以尝试force index hint - dev.mysql.com/doc/refman/5.6/en/index-hints.html,这应该会强制 MySql 使用该索引,然后将查询时间与 FTS 和 INDEX 进行比较。 【参考方案1】:

MySQL 优化器尝试做最快的事情。如果它认为使用索引将花费与执行表扫描一样长或更长的时间,它就会放弃可用的索引。

这就是您在示例中看到的:

如果范围较小(1 天),索引会更快; 如果范围很大,您将访问更多的表,您不妨直接扫描表(请记住,使用索引涉及搜索索引,然后从表中获取索引记录 -两组搜索)。

如果你认为你比优化器更了解(它并不完美),use INDEX hints:

USE INDEX (index_list) 提示告诉 MySQL 只使用其中一个 命名索引来查找表中的行。替代语法 IGNORE INDEX (index_list) 告诉 MySQL 不要使用某些特定的索引或 索引。如果 EXPLAIN 显示 MySQL 正在使用这些提示,这些提示很有用 可能索引列表中的错误索引。

【讨论】:

以上是关于时间戳列上的 MySQL 索引不用于大日期范围的主要内容,如果未能解决你的问题,请参考以下文章

在时间戳列上为使用年份函数的查询创建索引

使用 Pandas query() 过滤时间戳列上的数据帧

在谷歌表单时间戳列上查询“今天”?

主索引列上的 MySQL 表外键索引

Mysql 索引优化

TEXT 列上的 MySQL 索引无效