MySQL 中的 2 个相似查询,2 个非常不同的性能,为什么?

2 similar query in MySQL, 2 very different performance, why?

我们有一个相当大的数据库,我们在其中查询一组基于日期时间列的数据库。昨天我们遇到了一个问题,我们发现通常需要 4 秒的特定查询现在需要 40 多秒。

经过一些挖掘和调试,我们发现了问题所在。

mysql> explain select count(*) from event where survey_id = 158 and event_datez>'2019-10-30 00:00:00' and event_datez<'2019-11-28 23:59:59' ; # Query takes 4s
+----+-------------+--------------+------------+-------+-----------------------------------------------+------------------+---------+------+---------+----------+------------------------------------+
| id | select_type | table        | partitions | type  | possible_keys                                 | key              | key_len | ref  | rows    | filtered | Extra                              |
+----+-------------+--------------+------------+-------+-----------------------------------------------+------------------+---------+------+---------+----------+------------------------------------+
|  1 | SIMPLE      |        event | NULL       | range | FK_g1lx0ea096nqioytyhtjng72t, i_event_2       | i_event_2        | 6       | NULL | 2975160 |    50.00 | Using index condition; Using where |
+----+-------------+--------------+------------+-------+-----------------------------------------------+------------------+---------+------+---------+----------+------------------------------------+
1 row in set, 1 warning (0.00 sec)

mysql> explain select count(*) from event where survey_id = 158 and event_datez>'2019-10-29 00:00:00' and event_datez<'2019-11-28 23:59:59' ; # Query takes 40s
+----+-------------+--------------+------------+------+-----------------------------------------------+------------------------------+---------+-------+----------+----------+-------------+
| id | select_type | table        | partitions | type | possible_keys                                 | key                          | key_len | ref   | rows     | filtered | Extra       |
+----+-------------+--------------+------------+------+-----------------------------------------------+------------------------------+---------+-------+----------+----------+-------------+
|  1 | SIMPLE      | event        | NULL       | ref  | FK_g1lx0ea096nqioytyhtjng72t,i_event_2        | FK_g1lx0ea096nqioytyhtjng72t | 9       | const | 16272884 |    12.23 | Using where |
+----+-------------+--------------+------------+------+-----------------------------------------------+------------------------------+---------+-------+----------+----------+-------------+
1 row in set, 1 warning (0.00 sec)

问题是 InnoDB 更改了用于查询的索引。我的问题很简单,为什么会这样?

在一本书的索引中,为什么不包含像 "the" 或 "and" 这样的常用词?因为它会匹配书中的每一页,而且在索引中查找值也没有用。您不妨从头到尾阅读本书的所有页面。

如果

MySQL 估计条件会匹配大部分行,则不会使用索引。没有记录精确的阈值,但根据我的经验,它大约是 table 的 20-25%。请注意,MySQL 索引统计数据也不总是完美的;它们是基于抽样数据的估计。

在您的第二个查询中,日期的范围条件稍微宽一些。因此它匹配更多行。可能这刚好超过阈值,所以 MySQL 决定不使用 i_event_2 索引。

MySQL 也可能略微偏爱使用 type: ref 而不是 type: range 的查询优化计划。

您可以使用 index hint 使 MySQL 仅考虑 i_event_2 索引。

select count(*) from event USE INDEX (i_event_2)
where survey_id = 158
  and event_datez>'2019-10-29 00:00:00' 
  and event_datez<'2019-11-28 23:59:59' ;

但我认为在两列上创建一个复合索引会更好:

ALTER TABLE event ADD INDEX i_event_survey_datez (survey_id, event_datez);