索引条件下推(ICP)是一种针对 MySQL 使用索引从表中检索行的优化。如果没有 ICP,存储引擎会遍历索引以找到基表中的行,并将它们返回给 MySQL 服务器,后者会评估这些行的 WHERE
条件。启用 ICP 后,如果 WHERE
条件的某些部分仅使用索引中的列即可评估,则 MySQL 服务器会将这部分 WHERE
条件下推到存储引擎。然后,存储引擎使用索引条目评估下推的索引条件,并且只有在满足该条件时,才会从表中读取该行。ICP 可以减少存储引擎必须访问基表的次数,以及 MySQL 服务器必须访问存储引擎的次数。
索引条件下推优化的适用性取决于以下条件:
当需要访问完整的表行时,ICP 用于
range
、ref
、eq_ref
和ref_or_null
访问方法。对于
InnoDB
表,ICP 仅用于辅助索引。ICP 的目标是减少全行读取的次数,从而减少 I/O 操作。对于InnoDB
聚簇索引,完整的记录已经被读取到InnoDB
缓冲区中。在这种情况下,使用 ICP 不会减少 I/O。在虚拟生成列上创建的辅助索引不支持 ICP。
InnoDB
支持在虚拟生成列上创建辅助索引。不能下推引用子查询的条件。
不能下推引用存储函数的条件。存储引擎无法调用存储函数。
不能下推触发条件。(有关触发条件的信息,请参阅 第 10.2.2.3 节,“使用 EXISTS 策略优化子查询”。)
不能将条件下推到包含对系统变量引用的派生表。
要了解此优化的工作原理,请首先考虑在不使用索引条件下推的情况下索引扫描如何进行:
获取下一行,首先读取索引元组,然后使用索引元组定位和读取完整的表行。
测试适用于此表的
WHERE
条件部分。根据测试结果接受或拒绝该行。
使用索引条件下推,扫描将按如下方式进行:
获取下一行的索引元组(但不是完整的表行)。
测试适用于此表且仅使用索引列即可检查的
WHERE
条件部分。如果不满足条件,则继续处理下一行的索引元组。如果满足条件,则使用索引元组定位和读取完整的表行。
测试适用于此表的
WHERE
条件的剩余部分。根据测试结果接受或拒绝该行。
当使用索引条件下推时,EXPLAIN
输出在 Extra
列中显示 Using index condition
。它不会显示 Using index
,因为当必须读取完整的表行时,该信息不适用。
假设一个表包含有关人员及其地址的信息,并且该表有一个定义为 INDEX (zipcode, lastname, firstname)
的索引。如果我们知道一个人的 zipcode
值,但不确定姓氏,则可以像这样进行搜索:
SELECT * FROM people
WHERE zipcode='95054'
AND lastname LIKE '%etrunia%'
AND address LIKE '%Main Street%';
MySQL 可以使用索引扫描 zipcode='95054'
的人员。第二部分(lastname LIKE '%etrunia%'
)不能用于限制必须扫描的行数,因此在没有索引条件下推的情况下,此查询必须检索 zipcode='95054'
的所有人员的完整表行。
使用索引条件下推,MySQL 会在读取完整的表行之前检查 lastname LIKE '%etrunia%'
部分。这避免了读取与 zipcode
条件匹配但与 lastname
条件不匹配的索引元组对应的完整行。
索引条件下推默认启用。可以通过设置 optimizer_switch
系统变量中的 index_condition_pushdown
标志来控制它。
SET optimizer_switch = 'index_condition_pushdown=off';
SET optimizer_switch = 'index_condition_pushdown=on';