我正在尝试优化查询,但我看到了一种我不理解的奇怪行为。
我有一个包含近200万条记录的表,其中“status_code”列的索引是tinyint。
当我在In子句中用10个数字调用查询时,mysql会进行全表扫描
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);
我正在使用Amazon RDS,我需要了解为什么会出现这种情况,以及是否有任何类型的配置可以控制它。
答案 0 :(得分:1)
当您使用IN()
谓词时,MySQL必须分析列表中每个值的索引,估计使用索引的好处。当您使用长值列表时,即使在执行查询之前,计算优化程序的估计也会变得很昂贵。
在MySQL 5.6中,他们建立了一个阈值,因此IN()
谓词中的10个或更多项的列表跳过了每个值的索引潜值工作,并且只是猜测了使用索引的值。以前收集的有关指数的统计数据。这里记录了这一点: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。查看db参数组。
答案 1 :(得分:0)
使用索引是由统计数据驱动的。我没有MySQL的精确信息,但如果计算结果大于表的2%,PostgreSQL将进行seq-scan。在你的情况下,它可以是其他值,但机制是相同的。
数据库使用统计信息来查看您的查询是否返回超过表的小百分比 - 在这种情况下 - 使用序列读取。如果表小于5MB,MS SQL Server将不使用索引 - 这更快。我的意思是 - 这是典型的,并且所有RDBMS都是这样的。有时失败 - 正如你所看到的那样。
怎么办?您可以analyze table
更新统计信息。您可以使用提示use_stat_tables
关闭存储的统计数据...在PostgreSQL中,您可以更改表格的直方图以获得更精确的结果,但我不知道MySQL的那些。还有很多驱动程序,这个特殊问题可以在这个级别解决。
提供explain
s在这里不会有太大变化。 MySQL的解释很差,问题的性质也很明显。
作为旁注。这与RDS无关 - 这是RDBMS的典型问题。不同的系统以不同的方式处理它,MySQL不是这里的领导者。