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);
我们有一个相当大的数据库,我们在其中查询一组基于日期时间列的数据库。昨天我们遇到了一个问题,我们发现通常需要 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);