索引条件下推 (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';
请参见 第 10.9.2 节,“可切换优化”。