IN 子句中的项数如何与索引使用相关

How number of items in IN clause relates to index usage

我正在尝试优化查询,但我看到了一个我不理解的奇怪行为。

我有一个 table 包含将近 200 万条记录,在列 "status_code" 上有一个索引,它是 tinyint。

当我在 In 子句中使用 10 个数字调用查询时,mysql 执行完整的 table 扫描

select * from `table` as t
where t.code in (1,2,3,4,5,6,7,8,9,10);

当我在IN子句中调用9个数字时,使用索引。

select * from `table` as t
where t.code in (1,2,3,4,5,6,7,8,9);

注意:虽然查询 returns 只有 1 行,但这里的行数是 '9'。

我正在使用 Amazon RDS,我需要了解为什么会出现这种行为,以及是否有任何类型的配置可以控制这种情况。

使用索引是由统计数据驱动的。我没有 MySQL 的准确信息,但如果计算结果大于 table 的 2%,PostgreSQL 将进行顺序扫描。在您的情况下,它可以是其他值,但机制是相同的。

DB 使用统计信息来查看您的查询 returns 是否超过 table 的一小部分 - 在这种情况下 - 使用序列读取。如果 table 小于 5MB,则 MS SQL 服务器将不会使用索引 - 即更快。我的意思是 - 这是典型的,所有 RDBMS 都是这样。有时会失败 - 如您所见。

怎么办?您可以 analyze table 更新统计信息。您可以使用提示 use_stat_tables 关闭存储的统计信息...在 PostgreSQL 中,您可以更改 table 的直方图以获得更精确的结果,但我不知道类似的事情对于 MySQL。也有很多驱动程序,这个特殊问题可以在这个级别解决。

提供 explains 在这里不会有太大变化。 MySQL解释得不好,问题的本质也很明显。

作为旁注。这与 RDS 无关——这是 RDBMS 的典型问题。不同的系统以不同的方式处理它,MySQL 不是这里的领导者。

当您使用 IN() 谓词时,MySQL 必须分析列表中每​​个值的索引,估计使用索引的好处。当您使用长值列表时,即使在执行查询之前计算优化器的估计值也会变得很昂贵。

在 MySQL 5.6 中,他们建立了一个阈值,因此 IN() 谓词中包含 10 个或更多项的列表会跳过按值索引的工作,而只是猜测值根据先前收集的有关索引的统计信息使用索引。这在此处记录:"Equality Range Optimization of Many-Valued Comparisons."

小节中的 https://dev.mysql.com/doc/refman/5.6/en/range-optimization.html

您可以使用变量 eq_range_index_dive_limit 调整阈值。在MySQL 5.6中,默认是10。在MySQL 5.7中,他们意识到默认的10太小了,所以他们将默认值增加到200。你可以把这个变量改成200喜欢 MySQL 5.7 行为。

我注意到您正在使用 RDS。 RDS 上的默认值有时与库存 MySQL 中的默认值不同,因此即使您使用基于 MySQL 5.7 的 RDS,默认值也可能是 10。检查您的数据库参数组。