惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

罗磊的独立博客
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
Hugging Face - Blog
Hugging Face - Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
WordPress大学
WordPress大学
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
博客园 - Franky
博客园 - 司徒正美
酷 壳 – CoolShell
酷 壳 – CoolShell
爱范儿
爱范儿
Jina AI
Jina AI
Last Week in AI
Last Week in AI
雷峰网
雷峰网
IT之家
IT之家
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 聂微东
小众软件
小众软件
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V
V2EX

博客园 - 幽州散人

SQL优化案例 synchronized 为啥会让虚拟线程卸载失效 主线任务和使命 温州3日之旅 CompletableFuture + 异步Servlet:同步的姿势、异步的效果 Snowflake雪花算法与发号器的应用 windows Java 幽灵转发壳进程 也谈MySQL limit offset深翻页问题 SHOW SESSION STATUS 与 Handler 变量 MySQL 5.7 Profiles 执行性能分析 MySQL临时表与文件排序 MySQL 5.7 MRR多范围读优化 MySQL符合索引与最左前缀原则 从I/O 的物理成本理解回表和索引覆盖 第4个电瓶以及补漆 Flink原理:并行度与keyGroup桶 什么是数字批发银行 Java虚拟线程(三)实现原理 Java虚拟线程(二) Java虚拟线程(一) 大模型推理层服务化架构 传奇调查员之路:理智值-99,但我必须听懂深渊的语言 js里调用智能合约读/写函数的方法的区别 字节与其16进制字符表示转换的bug Redisson分布式锁 交易心得 DexScreener接口初探 某安全软件跑飞了。。 Ed25519算法签名与验签的Java实现 Tendermint拜占庭容错引擎
MySQL 5.7 ICP索引条件下推优化
幽州散人 · 2026-08-13 · via 博客园 - 幽州散人

ICP索引条件下推是5.7默认的一个优化,
意思是把MySQL Server 层的过滤"下推"到存储引擎,用索引叶子节点上现成的列值提前过滤,换取回表次数的减少。它不如复合索引的"精确定位"高效,但远好于全量回表再过滤。
先要明白MySQL的架构:

┌─────────────────────────────────────────────┐
│           客户端层 (Client Layer)             │
│   mysql CLI / DBeaver / JDBC / ODBC          │
├─────────────────────────────────────────────┤
│          服务层 (MySQL Server Layer)         │
│  ┌─────────────────────────────────────┐    │
│  │   连接管理 / 线程处理 (Connection Pool) │   │
│  ├─────────────────────────────────────┤    │
│  │   查询缓存 (Query Cache) ← 5.7 有/8.0 移除│ │
│  ├─────────────────────────────────────┤    │
│  │   解析器 (Parser) → 词法/语法分析      │    │
│  ├─────────────────────────────────────┤    │
│  │   优化器 (Optimizer) → 执行计划选择    │    │
│  ├─────────────────────────────────────┤    │
│  │   执行器 (Executor) → 逐行读取/过滤/聚合│    │
│  └─────────────────────────────────────┘    │
├─────────────────────────────────────────────┤
│        存储引擎层 (Storage Engine Layer)      │
│     InnoDB (5.5+ 默认)                      │
│     数据文件 / Redo Log / Undo Log / Buffer Pool │
├─────────────────────────────────────────────┤
│            文件系统 (File System)            │
└─────────────────────────────────────────────┘

这是分层架构,Server层执行器Executor去存储引擎搂数据、返回客户端之前,会先过滤。没在索引机制用上的查询条件用在这里的过滤。
整条过滤流水线是这样的:


WHERE name LIKE 'A%' AND age = 25 AND status = 1 AND email IS NOT NULL
      └──────┬──────┘   └────┬───┘   └────┬────┘   └──────┬──────┘
             │               │             │               │
      B+Tree定位范围      ICP下推      Server层过滤      Server层过滤
      (存储引擎)      (存储引擎)    (执行器)        (执行器)
             │               │             │               │
             ▼               ▼             ▼               ▼
       只扫描 A% 的    跳过 age≠25    回表后的行上     回表后的行上
       索引区间        的索引条目      逐行判断        逐行判断

EXPLAIN 里怎么区分:

┌───────────────────────┬───────────────────────┬──────────────────────────────────────┐
│     过滤发生在哪        │      Extra 显示       │                 说明                  │
├───────────────────────┼───────────────────────┼──────────────────────────────────────┤
│ 全部在存储引擎完成       │ 无 Using where        │ 索引条件+ICP覆盖了所有 WHERE 条件        │
├───────────────────────┼───────────────────────┼──────────────────────────────────────┤
│ 有剩余条件交给 Server   │ Using where           │ 回表取到行后,执行器逐行判断剩余条件       │
├───────────────────────┼───────────────────────┼──────────────────────────────────────┤
│ ICP 生效               │ Using index condition │ 部分条件在索引叶子层面就过滤了            │
└───────────────────────┴───────────────────────┴──────────────────────────────────────┘

所以在 EXPLAIN 里看到 Using where,就知道:有部分 WHERE 条件没能在索引机制里消化掉,是执行器把行捞回来之后才过滤的。

好,我们接着讲解一下ICP的原理。

INDEX idx_name_age (name, age)

SELECT * FROM users WHERE name LIKE 'A%' AND age = 25;
--                                    ↑                ↑
--                            索引可以用       ICP 在索引层面过滤 age
--
-- 没有 ICP:name LIKE 'A%' 的全部回表,Server 再过滤 age=25
-- 有 ICP:  索引遍历时直接跳过 age≠25 的,只回表 age=25 的
--
-- EXPLAIN Extra: "Using index condition"

上面例子的复合索引,按最左前缀原则,只用到了name索引列,age失效。 但是查询条件又确实用到了2个索引列,ICP可以拉一把。

按照name LIKE 'A%'去二级索引找到匹配的所有主键,再去聚集索引找到这些主键对应的行。
然后从存储引擎返回MySQL Server,执行器 (Executor) → 逐行读取,带age=25条件进行过滤。
以上是没有ICP的时候的情况。

而有了ICP,在去二级索引按照name LIKE 'A%'找匹配的主键的时候,顺便就会用age=25把数据进行过滤。
然后回表的时候,回表条数就会少很多。 MySQL Server那里也不用过滤了。
相当于把MySQL Server 执行器的过滤,搬到了二级索引过滤,但是减少了回表条数,提高了效率和性能。

当然,这也没有复合索引生效那么高效,
区别在于,ICP情形是获取到前一个索引列对应的主键所在的叶子节点之后,对这些记录进行逐条遍历,然后进行过滤。
复合索引是找到前一个索引列对应的主键记录后,按后一个索引列找到age=25按顺序进行批量读取。
如下所示:

ICP 情形:  在【二级索引自己的叶子节点】上逐条遍历
           (每个叶子条目 = name + age + PK)
           读到一条 → 看自带的 age → 25 就记下 PK 去回表,≠25 跳过
           特点:name LIKE 'A%' 区间内的叶子条目【全都读过一遍】

复合索引情形:B+Tree 树形查找直接定位到 (name, age=25) 的起始叶子位置
           age≠25 的叶子条目【根本不会被读到】
           特点:跳过不读 vs 读过再过滤 —— 这就是差距