优化MySQL查询:IN列表的长列表首先缩小搜索范围

时间:2017-08-25 15:55:21

标签: mysql sql select optimization

当使用大的IN列表进行SELECT时,首先缩小搜索范围会更快吗?

假设:

  • id是主键
  • 表有2M行
  • @list_of_id中的ID始终靠近,但不是顺序的(选择在同一个月创建的一组人员的子集)

当@list_of_id为1K长,10K长或100K长时,以下查询应如何执行?

查询1:

SELECT * FROM people
WHERE id IN (@list_of_id);

查询2a(首先缩小搜索范围):

SELECT * FROM people
WHERE id <= @max_id
    AND id >= @min_id
    AND id IN (@list_of_id);

查询2b(可能更慢):

SELECT * FROM people
WHERE MONTH(created) = @month
    AND id IN (@list_of_id);

2 个答案:

答案 0 :(得分:2)

MySQL 5.6专门针对IN()谓词中的长ID列表引入了一些优化器改进。阅读https://dev.mysql.com/doc/refman/5.6/en/range-optimization.html#equality-range-optimization

如果您依赖于IN()搜索的索引,则该优化是相关的。如果您在查询2a中首先缩小搜索范围,则索引将用于不等式表达式,但不用于IN()谓词。

通常,当您有多个搜索词时,只会使用索引优化一个范围谓词。范围谓词不是=

所以在你的2a示例中,BETWEEN谓词将使用索引(我相信你使用的不等式表达式将被优化,好像它是一个BETWEEN谓词),然后是{ {1}}谓词只会线性搜索第一个索引扫描的结果。

在您的2b示例中,尝试使用IN()缩小搜索范围时根本无法使用索引。考虑在电话簿中查找人员 - 如果我要求您找到中间首字母为“J”的所有人那么电话簿按姓氏排序的事实对你没有帮助。

您可能希望查看我的演示文稿How to Design Indexes, Really或视频:https://www.youtube.com/watch?v=ELR7-RdU9XU

答案 1 :(得分:0)

使用查询1。

一个简单的IN(long-list)将越过索引(在你的情况下为PRIMARY KEY)。这比任何替代方案都更有效。

您可以通过

'证明'这一点
FLUSH STATUS;
SELECT ...
SHOW SESSION STATUS LIKE 'Handler%';

当我在IN中使用146个项目进行尝试时,我得到了

+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| Handler_commit             | 1     |
| Handler_delete             | 0     |
| Handler_discover           | 0     |
| Handler_external_lock      | 2     |
| Handler_mrr_init           | 0     |
| Handler_prepare            | 0     |
| Handler_read_first         | 0     |
| Handler_read_key           | 146   |  <-- note
... (other things with "0")

这说明它实际上只探测了146次指数。

我在IN列表中看到过70K项目。查询花了一些时间,但考虑到结果集有70K行,它并没有死,甚至相当快。

(警告:我测试了5.6.22;一些旧版本(5.1?)可能效率较低。)

如果您的ID列表已经在另一个表中,那么JOIN到该表。这比从服务器中取出ID并将它们发送回服务器要快。