MySQL的索引下推(ICP)

Index Condition Pushdown 是 MySQL 5.6 引入的查询优化特性,用于减少回表次数,提升查询性能。


核心思想

传统查询流程(无 ICP)

复制代码
存储引擎          MySQL Server
   ↓                  ↓
读取索引 → 回表查整行 → Server层过滤数据
   ↑___________________________|
        (多次回表,大量无效IO)

ICP 优化后流程

复制代码
存储引擎(含ICP)
   ↓
读取索引 → 在引擎层直接过滤 → 只回表有效数据
   ↑___________________________|
        (减少回表,减少IO)

本质 :把 WHERE 条件下推到存储引擎层,在不回表的情况下先过滤索引记录。


实战示例

场景设定

复制代码
-- 表结构
CREATE TABLE users (
    id INT PRIMARY KEY,
    user_name VARCHAR(20),
    user_age INT,
    user_level VARCHAR(10),
    -- 其他字段...
    INDEX idx_name_age (user_name, user_age)  -- 联合索引
);

-- 查询:使用ICP优化
SELECT * FROM users 
WHERE user_name = 'tom' AND user_age > 17;

执行过程对比

步骤 无 ICP 有 ICP
1 根据 user_name='tom' 找到索引记录 根据 user_name='tom' 找到索引记录
2 直接回表获取完整行数据 在引擎层用 user_age > 17 过滤索引
3 Server层用 user_age > 17 过滤 只回表满足条件的记录
4 丢弃不满足条件的行 返回结果

数据示意图

复制代码
索引数据 (user_name, user_age, id):
┌─────────┬──────────┬────┐
│  tom    │    15    │ 1  │  ← 不满足 age>17,无ICP会回表,有ICP直接跳过
│  tom    │    18    │ 2  │  ← 满足条件,回表
│  tom    │    20    │ 3  │  ← 满足条件,回表  
│  tom    │    16    │ 4  │  ← 不满足,跳过
└─────────┴──────────┴────┘

无ICP:回表4次,Server过滤掉2条
有ICP:只回表2次,引擎层已过滤

ICP 触发条件

启用条件

复制代码
-- 查看是否开启(默认开启)
SHOW VARIABLES LIKE 'optimizer_switch';
-- 应包含:index_condition_pushdown=on

使用条件

条件 说明
存储引擎 InnoDB 和 MyISAM 支持
索引类型 二级索引(非聚簇索引)
查询类型 range, ref, eq_ref, ref_or_null
条件位置 WHERE 中的条件可以下推

不使用 ICP 的情况

复制代码
-- 1. 聚簇索引(主键索引)
SELECT * FROM users WHERE id > 10 AND age = 20;  -- 主键查询不需要ICP

-- 2. 覆盖索引(不需要回表)
SELECT user_name, user_age FROM users 
WHERE user_name = 'tom' AND user_age > 17;  -- Extra 会显示 Using index

-- 3. 子查询
SELECT * FROM users WHERE user_name IN (SELECT...);

-- 4. 存储函数/触发器条件
WHERE user_name = func(some_col);  -- 无法下推

EXPLAIN 识别 ICP

复制代码
EXPLAIN SELECT * FROM users 
WHERE user_name = 'tom' AND user_age > 17;

关键字段解读

字段 无 ICP 有 ICP
type range range
key idx_name_age idx_name_age
Extra Using where Using index condition

Using index condition 就是 ICP 的标志!


联合索引与 ICP 的配合

经典场景:索引列范围查询后的条件

复制代码
-- 索引:(user_name, user_age, user_level)

-- 场景1:范围查询阻断索引
SELECT * FROM users 
WHERE user_name = 'tom' 
  AND user_age > 17           -- 范围查询,user_level 无法走索引
  AND user_level = 'A';        -- 但可以用 ICP 下推!

执行流程

  1. user_name = 'tom' → 走索引第1列

  2. user_age > 17 → 走索引第2列(范围)

  3. user_level = 'A'无法走索引 ,但可用 ICP 下推到引擎层过滤

    Extra 显示:Using index condition
    (不是 Using where,说明用了ICP优化)

场景对比

SQL 索引使用 ICP 作用
WHERE name='tom' AND age=18 AND level='A' 3列全中 不需要ICP(索引已完全过滤)
WHERE name='tom' AND age>17 AND level='A' 前2列 ICP 过滤 level
WHERE name='tom' AND age>17 前2列 不需要ICP(无后续条件)

性能收益量化

假设 user_name='tom' 有 1000 条记录:

场景 回表次数 IO 开销
无 ICP,有 user_age > 17 过滤 1000 次
有 ICP,user_age > 17 在引擎层过滤 ~500 次(假设一半满足) 降低 50%
再加 user_level = 'A' 可能只需 100 次 降低 90%

总结

Index Condition Pushdown 索引条件下推

  • 作用:减少回表次数,降低磁盘IO
  • 标志:EXPLAIN 显示 "Using index condition"
  • 条件:二级索引 + 范围查询后的过滤条件
  • 版本:MySQL 5.6+(默认开启)

一句话记忆 :ICP 让存储引擎在不回表的情况下 ,用索引之外的 WHERE 条件提前过滤数据,只把"可能合格"的数据回表给 Server 层

相关推荐
瞬间&永恒~21 小时前
【MySQL】4-6:在同一个主机上使用 systemd 运行多个 MySQL服务器
服务器·数据库·mysql
AAA@峥1 天前
CentOS7 源码编译安装 MySQL5.7|SQL 基础操作 + 备份恢复完整实战
运维·数据库·sql·centos
一嘴一个橘子1 天前
MysqL 哪些情况适合创建索引
mysql
时间的拾荒人1 天前
MySQL C语言连接 - 从入门到实战
android·c语言·mysql
Database_Cool_1 天前
云数据库控制台好不好用、能不能可视化操作:阿里云 RDS MySQL 控制台体验详解
数据库·mysql·阿里云
番茄炒鸡蛋加糖1 天前
MySQL 实战调优& 分表基础
数据库·mysql
万亿少女的梦1681 天前
基于Spring Boot的游戏交易管理系统设计与实现
java·spring boot·mysql·系统设计·交易管理
Database_Cool_1 天前
云数据库如何保证高可用、故障了怎么办:阿里云 RDS MySQL 高可用架构详解
数据库·mysql·阿里云
Mem0rin1 天前
[MySQL] 聚合函数、分组查询、连接查询
android·mysql