MySql没有为几个查询选择正确的索引
我在表上运行folling查询,在where条件下更改值,同时在一种情况下运行它使用一个索引,另一种情况下使用另一个(错误的??)索引 查询1的行数为402954,大约需要1.5秒 查询2的行数为52097大约需要35秒 查询1和查询2都是相同的,只是我在where条件中更改了值 问题1MySql没有为几个查询选择正确的索引,mysql,sql,query-optimization,database-indexes,Mysql,Sql,Query Optimization,Database Indexes,我在表上运行folling查询,在where条件下更改值,同时在一种情况下运行它使用一个索引,另一种情况下使用另一个(错误的??)索引 查询1的行数为402954,大约需要1.5秒 查询2的行数为52097大约需要35秒 查询1和查询2都是相同的,只是我在where条件中更改了值 问题1 EXPLAIN SELECT log_type,count(DISTINCT subscriber_id) AS distinct_count, count(subscriber_id)
EXPLAIN SELECT
log_type,count(DISTINCT subscriber_id) AS distinct_count,
count(subscriber_id) as total_count
FROM campaign_logs
WHERE
domain = 'xxx' AND
campaign_id='123' AND
log_type IN ('EMAIL_SENT', 'EMAIL_CLICKED', 'EMAIL_OPENED', 'UNSUBSCRIBED') AND
log_time BETWEEN
CONVERT_TZ('2015-02-12 00:00:00','+05:30','+00:00') AND
CONVERT_TZ('2015-02-19 23:59:58','+05:30','+00:00')
GROUP BY log_type;
解释上述问题
+----+-------------+---------------+-------+------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------+-------+------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+------+--------+-------------+
| 1 | SIMPLE | campaign_logs | range | campaign_id_index,domain_index,log_type_index,log_time_index,campaignid_domain_logtype_logtime_index | campaignid_domain_logtype_logtime_index | 468 | NULL | 402954 | Using where |
+----+-------------+---------------+-------+------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+------+--------+-------------+
+----+-------------+---------------+-------------+------------------------------------------------------------------------------------------------------+--------------------------------+---------+------+-------+------------------------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------+-------------+------------------------------------------------------------------------------------------------------+--------------------------------+---------+------+-------+------------------------------------------------------------------------------+
| 1 | SIMPLE | campaign_logs | index_merge | campaign_id_index,domain_index,log_type_index,log_time_index,campaignid_domain_logtype_logtime_index | campaign_id_index,domain_index | 153,153 | NULL | 52097 | Using intersect(campaign_id_index,domain_index); Using where; Using filesort |
+----+-------------+---------------+-------------+------------------------------------------------------------------------------------------------------+--------------------------------+---------+------+-------+------------------------------------------------------------------------------+
问题2
EXPLAIN SELECT
log_type,count(DISTINCT subscriber_id) AS distinct_count,
count(subscriber_id) as total_count
FROM stats.campaign_logs
WHERE
domain = 'yyy' AND
campaign_id='345' AND
log_type IN ('EMAIL_SENT', 'EMAIL_CLICKED', 'EMAIL_OPENED', 'UNSUBSCRIBED') AND
log_time BETWEEN
CONVERT_TZ('2014-02-05 00:00:00','+05:30','+00:00') AND
CONVERT_TZ('2015-02-19 23:59:58','+05:30','+00:00')
GROUP BY log_type;
解释上述问题
+----+-------------+---------------+-------+------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------+-------+------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+------+--------+-------------+
| 1 | SIMPLE | campaign_logs | range | campaign_id_index,domain_index,log_type_index,log_time_index,campaignid_domain_logtype_logtime_index | campaignid_domain_logtype_logtime_index | 468 | NULL | 402954 | Using where |
+----+-------------+---------------+-------+------------------------------------------------------------------------------------------------------+-----------------------------------------+---------+------+--------+-------------+
+----+-------------+---------------+-------------+------------------------------------------------------------------------------------------------------+--------------------------------+---------+------+-------+------------------------------------------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------+-------------+------------------------------------------------------------------------------------------------------+--------------------------------+---------+------+-------+------------------------------------------------------------------------------+
| 1 | SIMPLE | campaign_logs | index_merge | campaign_id_index,domain_index,log_type_index,log_time_index,campaignid_domain_logtype_logtime_index | campaign_id_index,domain_index | 153,153 | NULL | 52097 | Using intersect(campaign_id_index,domain_index); Using where; Using filesort |
+----+-------------+---------------+-------------+------------------------------------------------------------------------------------------------------+--------------------------------+---------+------+-------+------------------------------------------------------------------------------+
查询1使用了正确的索引,因为我有复合索引
查询2正在使用索引合并,执行时间很长
为什么MySql对同一查询使用不同的索引
我知道我们可以在查询中提到使用索引,但是为什么MySql在这种情况下没有选择正确的索引呢??。我做错什么了吗?不,你没有做错什么 正如Chipmonkey在评论中所说的,有时MySQL会因为过时的表统计信息而选择错误的执行计划。您可以通过执行
分析表格
来更新表格统计信息
不过,MySQL优化器并没有那么复杂。它发现,在这两种情况下,MySQL都必须访问二级索引,然后对聚集索引执行查找以获得实际的表数据,因此当它看到第二个查询通过使用两个单独的索引并合并它们而具有更好的选择性时,您不能因为猜测错误而过分责怪它
我猜如果你有一个覆盖索引,这样MySQL就可以只使用索引执行整个查询,那么它会倾向于使用该索引而不是执行合并
尝试将subscriber\u id
添加到多列索引的末尾,以获得覆盖索引
否则,请使用
使用索引
或强制索引
,因为这就是它们的用途。您比MySQL更了解数据。不,您没有做错任何事情
正如Chipmonkey在评论中所说的,有时MySQL会因为过时的表统计信息而选择错误的执行计划。您可以通过执行分析表格
来更新表格统计信息
不过,MySQL优化器并没有那么复杂。它发现,在这两种情况下,MySQL都必须访问二级索引,然后对聚集索引执行查找以获得实际的表数据,因此当它看到第二个查询通过使用两个单独的索引并合并它们而具有更好的选择性时,您不能因为猜测错误而过分责怪它
我猜如果你有一个覆盖索引,这样MySQL就可以只使用索引执行整个查询,那么它会倾向于使用该索引而不是执行合并
尝试将subscriber\u id
添加到多列索引的末尾,以获得覆盖索引
否则,请使用
使用索引
或强制索引
,因为这就是它们的用途。您比MySQL更了解数据。我建议您尝试以下方法:
添加复合索引的这种排列
(campaign_id,domain,log_time,log_type,subscriber_id)
更改查询以删除WHERE log\u type IN()
条件,从而允许聚合函数使用它在log\u time
上的范围扫描中找到的所有记录。在索引中包括subscriber\u id
,应该允许直接从索引中满足整个查询。也就是说,这是一个覆盖指数
最后,您可以通过将整个查询包装到
SELECT *
FROM (/*the whole query*/) x
WHERE log_type IN
('EMAIL_SENT', 'EMAIL_CLICKED', 'EMAIL_OPENED', 'UNSUBSCRIBED')
ORDER BY log_type
这将为您提供更好、更可预测的性能
(除非您想要的日志类型是记录的一小部分,否则请忽略此建议。)我建议您尝试以下方法: 添加复合索引的这种排列
(campaign_id,domain,log_time,log_type,subscriber_id)
更改查询以删除WHERE log\u type IN()
条件,从而允许聚合函数使用它在log\u time
上的范围扫描中找到的所有记录。在索引中包括subscriber\u id
,应该允许直接从索引中满足整个查询。也就是说,这是一个覆盖指数
最后,您可以通过将整个查询包装到
SELECT *
FROM (/*the whole query*/) x
WHERE log_type IN
('EMAIL_SENT', 'EMAIL_CLICKED', 'EMAIL_OPENED', 'UNSUBSCRIBED')
ORDER BY log_type
这将为您提供更好、更可预测的性能
(除非您想要的日志类型是记录的一小部分,在这种情况下,请忽略此建议。)有时候,像这样糟糕的查询优化与糟糕的统计数据有关——可能会有很多问题,MySQL将其记录在此处:。考虑运行分析表来更新关于索引分布的统计信息,然后重新运行解释。有时这样的坏查询优化与坏的统计有关-有很多可以继续的,MySQL文档在这里:考虑运行分析表来更新索引分布的统计信息,然后重新运行解释。@亚当斯,谢谢您的评论,表有2500万+记录,它有8列,所以我的问题是在5列上有覆盖索引是安全的,我怀疑它会增加磁盘空间。它将增加磁盘空间(理想情况下是内存占用)。还有一个专栏是什么?这里还有一个专栏。这个问题是修辞性的。索引中已有4列。为什么现在要担心5列?@Adams,谢谢你的评论,这个表有2500多万条记录,它有8列,所以我的问题是在5列上有覆盖索引是否安全,我怀疑它会增加磁盘空间。是的,它会增加磁盘空间(理想情况下是内存占用)。还有一个专栏是什么?这里还有一个专栏。这个问题是修辞性的。索引中已有4列。为什么现在要担心5列?这是真的。MySQL只能对第一个范围内的搜索使用索引。删除日志类型sh的范围要求