mysql:为什么in子句中的第5个id会极大地改变查询计划?

t3irkdon  于 2021-06-23  发布在  Mysql
关注(0)|答案(3)|浏览(311)

给出以下两个问题:
查询#1

SELECT log.id
FROM log
WHERE user_id IN
      (188858, 188886, 189854, 203623, 204072)
      and type in (14, 15, 17)
ORDER BY log.id DESC
LIMIT 25 OFFSET 0;

查询#2-4 ID而不是5

SELECT log.id
FROM log
WHERE user_id IN
      (188858, 188886, 189854, 203623)
      and type in (14, 15, 17)
ORDER BY log.id DESC
LIMIT 25 OFFSET 0;

解释计划

-- Query #1
1   SIMPLE  log range   idx_user_id_and_log_id  idx_user_id_and_log_id  4       41280   Using index condition; Using where; Using filesort
-- Query #2
1   SIMPLE  log index   idx_user_id_and_log_id  PRIMARY                 4       53534   Using where

为什么添加一个id会使执行计划如此不同?我说的是毫秒到1分钟的时间差。我想这可能和 eq_range_index_dive_limit 参数,但它仍然小于10(默认值)。我知道我可以强制使用索引而不是 clustered index ,但我想知道mysql为什么这么做。
我应该试着去理解吗?或者有时不可能理解查询计划器的决策?
额外细节
表大小:11gb
行:1.08亿
mysql:5.6.7版本
从in子句中删除哪个id无关紧要。
索引: idx_user_id_and_log_id(user_id, id)

gpnt7bae

gpnt7bae1#

添加

INDEX(user_id, type, id),
INDEX(type, user_id, id)

每一个都是一个“覆盖”索引。因此,整个查询可以通过只查看一个索引来执行,而不涉及“数据”。
对于优化器,我有两个选择——希望它能够选择 user_id IN (...) 更具选择性或 type IN (...) 为了挑选更好的指标。
如果,在添加这些之后,您对 idx_user_id_and_log_id(user_id, id) , DROP 是的。
(不,我无法解释为什么查询2选择进行表扫描。)

omvjsjqw

omvjsjqw2#

加入更有效。
使用in运算符的值创建临时表。然后在表“log”和临时值表之间建立连接。
有关更多信息,请参阅此答案。

8hhllhi2

8hhllhi23#

如您所示,mysql有两种可选的查询计划用于 ORDER BY ... LIMIT n :
读取所有符合条件的行,对它们进行排序,然后从前面的n行中选择。
按排序顺序读取行,当找到n个符合条件的行时停止。
为了决定哪个选项更好,优化器需要估计where条件的过滤效果。这不是直截了当的,尤其是对于没有索引的列,或者对于值相关的列。在您的例子中,为了找到比优化器预期的前25个符合条件的行,可能需要按排序顺序读取更多的表。
在处理limit查询的方式上有了一些改进,这两种改进都出现在5.6的更高版本中(您运行的是预ga版本!),以及更新版本(5.7、8.0)。我建议您尝试升级到更高版本,看看这是否仍然是一个问题。
通常,如果您想了解查询规划器的决策,应该查看查询的优化器跟踪。

相关问题