ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

详解索引下推

详解索引下推 索引下推Index Condition PushdownICPMySQL 5.6 版本引入InnoDB / MyISAM 都支持默认开启。概括在索引遍历过程中把 WHERE 里能用到索引字段的条件直接在索引层过滤减少回表次数。一、没有 ICP 的时候怎么做假设联合索引idx_name_age (name, age)表结构CREATE TABLE user( id INT PRIMARY KEY, name VARCHAR(20), age INT, phone VARCHAR(11), INDEX idx_name_age(name, age) );查询 SQLSELECT * FROM user WHERE name张三 AND age18;无 ICP5.6 之前执行流程联合索引叶子节点保存(name, age, 主键id)根据name张三在索引树找到所有匹配name张三的索引记录不判断 age 条件拿着这些记录里的主键 id全部回表读取完整行数据回到服务层再过滤age18返回结果。问题很多name张三但是age18的数据白白做了回表IO 开销大。开启 ICP 之后根据name张三在索引树找到匹配索引记录在索引层直接判断索引字段条件age18只有索引记录同时满足 name 和 age才拿主键 id 去回表不满足直接丢弃回表读取完整行返回。✅ 核心优化点在索引页就过滤掉一部分数据减少回表次数。二、ICP 适用条件只能用于二级索引主键索引不生效主键索引本身就存完整数据不存在回表WHERE 条件里部分字段在索引中部分不在索引中条件是范围查询之后的索引列联合索引最左匹配范围后面的字段无法用于索引查找但可以用 ICP 过滤不能是聚合函数、子查询SELECT只查索引字段覆盖索引时不需要回表ICP 就没意义。❗ 关键区分索引查找Index seek用来定位索引起始位置只能用最左前缀遇到范围 between就停止索引下推 ICP范围后面的索引字段不能用来定位起点但可以用来过滤。三、经典场景示例场景 1联合索引 范围查询ICP 最典型场景索引idx_a_b_c(a, b, c)SQLSELECT * FROM t WHERE a10 AND b20 AND c30;联合索引最左匹配规则a10等值b20范围b 之后的 c 无法参与索引查找。a10等值匹配 → 定位到所有 a10 的这一段索引区间 ✅b20范围查询在 a10 这一组里找到第一个 b20 的位置然后顺序向后扫描一段连续索引。⚠️ 重点经过b20这个范围之后这一段扫描出来的数据里b 不再是一个固定值b 是 21、22、23…… 不断变化。原本只有当 b 固定不变的时候c 才是有序的现在 b 五花八门c 在这段范围里不再全局有序联合索引里c 的有序性仅建立在前面所有字段全部相等的前提下。a 固定b 固定 → c 有序a 固定b 是一个范围多个不同 b→ c 无序索引想要快速定位c30必须 c 是有序的才能二分查找。 现在 c 乱序了无法利用索引快速过滤 c只能通过索引找到a10 AND b20的所有行回表取出完整数据再在内存里过滤 c30对比演示更好理解索引idx(a,b,c)where a10 and b20 and c30a 等值b 等值c 等值 → 三列全部走索引定位 ✅where a10 and b20 and c30a 等值b 范围 → b 之后 c无法索引定位只能扫描后过滤 c ❌where a10 and b20 and c30a 等值b 等值c 范围 → c 是第一个范围c 后面无字段没问题 ✅优化方案如果业务经常axx AND bxx AND cxx调换索引顺序 把等值条件放范围前面idx_a_c_b(a,c,b)条件a10 AND c30 AND b20a 等值c 等值b 范围 等值列全部放在范围列之前就可以充分利用索引。没有 ICP找到所有a10, b20的索引记录 → 全部回表 → 在 MySQL 服务层过滤c30。开启 ICP找到a10, b20的索引记录 →在索引层直接判断 c30不满足直接丢掉只把满足 c30 的记录回表。这里c在范围b后面不能用于索引定位但是可以 ICP 过滤。看执行计划explainExtra字段会出现Using index condition代表 ICP 生效。Extra: Using index condition区分Using index覆盖索引不需要回表Using index condition索引下推 ICP 两个可以同时出现。场景 2ICP 不生效的场景① 条件字段不在索引里索引idx_a_b(a,b)SELECT * FROM t WHERE a1 AND b2 AND d5; -- d不在索引d5无法下推只能回表后过滤只有 a、b 可以在索引层处理。② 覆盖索引不需要回表SELECT a,b,c FROM t WHERE a10 AND b20 AND c30;查询字段全部在索引里不需要回表。此时 ICP 没有收益Extra 不会出现 Using index condition。③ 主键索引主键索引叶子节点保存整行数据本身没有回表操作ICP 无效。④ 子查询、函数操作SELECT * FROM t WHERE a10 AND b20 AND DATE(c)2026-01-01;DATE(c)函数字段发生隐式转换ICP 无法下推。四、ICP 的局限性ICP 只是过滤不能减少索引扫描行数explain 里的 rows 是预估扫描索引行数ICP 不会减少这个 rows只会减少回表次数ICP 只能过滤索引上存在的字段非索引字段条件必须回表后过滤对于 InnoDBICP 只支持二级索引ICP 无法优化排序、分组。五、开关控制-- 查看是否开启ICP show variables like optimizer_switch; -- 关闭ICP测试对比用 set optimizer_switch index_condition_pushdownoff; -- 开启ICP set optimizer_switch index_condition_pushdownon;六、一句话总结面试版索引下推 ICP 是 MySQL 5.6 引入的优化针对二级索引。当联合索引遇到范围查询后后面的索引字段不能用来定位索引起点但可以在索引页提前过滤数据减少回表次数降低随机 IO执行计划 Extra 显示Using index condition代表生效。ICP 不改变索引扫描行数只减少回表。七、面试常问对比ICP 和覆盖索引的区别ICP减少回表次数覆盖索引完全不需要回表。二者可以同时存在。ICP 会不会减少 explain 里的 rows不会rows 是预估索引扫描行数ICP 只是扫描到索引记录时提前过滤不减少索引扫描数量。
返回列表