索引条件下推(ICP)是 MySQL 检索表行时使用索引的一种优化。没有 ICP,存储引擎会遍历索引以定位基表中的行,并将其返回给 MySQL 服务器,以评估 WHERE
条件。启用 ICP 后,如果 WHERE
条件的一部分可以仅使用索引中的列来评估,那么 MySQL 服务器将该部分条件下推到存储引擎。然后,存储引擎使用索引条目来评估下推的索引条件,只有在满足条件时才读取表中的行。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';