Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL组合索引

MySQL组合索引使用及说明

作者:AlbenXie

这篇文章主要介绍了MySQL组合索引使用及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

一、索引下推

索引下推(Index Condition Pushdown,ICP) 是 MySQL 针对联合索引的一种查询优化机制,核心是将部分 WHERE 条件的过滤操作下推到存储引擎层,减少回表次数,提升查询效率。

1. 核心原理与对比

2. 适用场景与限制

3. 示例

假设有联合索引 idx_name_age (name, age),查询 SELECT * FROM user WHERE name LIKE '张%' AND age = 20;

二、没有索引下推功能时,创建联合索引的目的是什么

即使没有 ICP 功能,创建联合索引依然有核心价值,其本质是利用索引的有序性实现快速检索,减少全表扫描,核心作用体现在以下两点:

1. 满足最左前缀匹配,实现高效查询

联合索引遵循最左前缀原则,只要查询条件包含索引的最左字段,就能触发索引扫描,大幅减少需要遍历的数据量。

比如联合索引 idx_name_age (name, age),查询 WHERE name = '张三' 或 name LIKE '张%' 时,存储引擎会直接通过索引定位到符合 name 条件的记录区间,避免全表扫描,这是联合索引的核心优势,和 ICP 无关。

2. 覆盖索引场景下,完全避免回表

若查询的字段全部包含在联合索引中(覆盖索引),无论是否开启 ICP,存储引擎都能直接从索引中获取数据,无需回表查询主键对应的完整行。

比如 SELECT name, age FROM user WHERE name = '张三',联合索引 idx_name_age 已经包含查询所需的所有字段,此时查询效率极高,ICP 在此场景下无优化意义。

简单来说:ICP 是联合索引的 "增值优化",用于减少回表次数;而联合索引的核心价值是基于最左前缀的快速检索 + 覆盖索引的无回表查询,这两个核心作用不依赖 ICP 就能生效。

三、无索引覆盖和索引下推时,组合索引和单列索引的区别

即使没有覆盖索引索引下推(ICP)联合索引和单列索引也有本质区别,核心差异在于索引的有序性覆盖范围和过滤效率,具体体现在两点:

1. 遵循最左前缀原则,支持多字段的逐层过滤

联合索引(如 idx_name_age (name, age))是按照字段顺序构建的有序结构,查询时可以先通过 name 筛选出一个小范围,再在这个范围内基于 age 进一步筛选,比单独用 name 列的单列索引过滤后,在 Server 层遍历 age 条件的效率更高。

而单列索引(如 idx_name)只能筛选 name 条件,后续的 age 过滤完全依赖 Server 层回表后处理,中间会产生更多的无效数据传输。

2. 减少索引数量,降低维护成本

一个联合索引可以覆盖多个前缀字段的查询场景(如查 name、查 name+age),而如果用单列索引,需要为每个字段创建独立索引,会增加索引的存储开销和增删改操作的索引维护成本,还可能引发索引选择的冲突。

简单总结:联合索引的核心价值是多字段的层级化有序过滤,覆盖索引和 ICP 只是在此基础上的优化手段,没有它们,联合索引依然比单列索引在多条件查询时更高效。

四、组合索引中,最左匹配多字段逐层过滤和索引下推的区别

最左匹配多字段逐层过滤是联合索引的核心检索规则索引下推(ICP)是基于联合索引的查询优化手段,二者的设计目标、生效阶段、作用范围完全不同,具体区别如下:

1. 核心目标不同

2. 生效阶段与范围不同

3. 无 ICP 时的差异体现

以联合索引 idx_name_age(name, age) 查询 WHERE name LIKE '张%' AND age=20 为例:

简单总结:最左匹配是联合索引能用的前提,ICP 是联合索引用得更高效的优化

五、举例说明

我们以 MySQL 表 user 为例,创建表结构和联合索引,通过相同查询语句在 无 ICP 和 有 ICP 两种场景下的执行流程,对比最左匹配逐层过滤索引下推的差异。

1. 准备测试环境

-- 创建用户表
CREATE TABLE `user` (
  `id` INT PRIMARY KEY AUTO_INCREMENT,
  `name` VARCHAR(20) NOT NULL,
  `age` INT NOT NULL,
  `address` VARCHAR(50)
);

-- 创建联合索引 idx_name_age (最左字段 name,其次 age)
CREATE INDEX idx_name_age ON `user` (`name`, `age`);

-- 插入测试数据
INSERT INTO `user` VALUES 
(1, '张三', 20, '北京'),
(2, '张三', 25, '上海'),
(3, '张四', 20, '广州'),
(4, '李五', 22, '深圳');

2. 测试查询语句

执行查询:SELECT * FROM user WHERE name LIKE '张%' AND age = 20;这个查询的条件满足最左匹配(用到了索引最左字段 name),我们分别看 无 ICP 和 有 ICP 的执行流程。

3. 场景 1:关闭 ICP,仅最左匹配生效

MySQL 中可以通过 SET optimizer_switch='index_condition_pushdown=off'; 关闭 ICP。执行流程:

4. 场景 2:开启 ICP,最左匹配 + 索引下推 共同生效

通过 SET optimizer_switch='index_condition_pushdown=on'; 开启 ICP(MySQL 5.6+ 默认开启)。执行流程:

5. 核心差异总结

场景最左匹配作用索引下推作用回表次数
无 ICP(仅最左匹配)筛选出 name 符合条件的索引无作用,不参与 age 过滤3 次
有 ICP(两者结合)筛选出 name 符合条件的索引引擎层提前过滤 age 符合条件的索引2 次

问题扩展

疑问:不对啊,场景1中的组合索引两个字段都会生效啊,应该是逐层过滤,先通过name过滤再通过age过滤啊,返回给server层的应该也是两条数据才对啊

核心误区在于:只有等值匹配的联合索引才能实现多字段逐层过滤,LIKE '张%' 这种范围匹配会截断联合索引的后续字段生效

结合之前的测试场景详细拆解:

1. 联合索引的逐层过滤有前提

2. 范围匹配截断了age字段的索引过滤(无 ICP 时)

3. ICP 的核心作用就是打破这个限制

简单总结:等值匹配的联合索引能逐层过滤,范围匹配会截断后续字段的索引过滤;ICP 则是让范围匹配后的字段,也能在引擎层基于索引数据过滤

六、联合索引字段生效规则速查表

以联合索引 idx_a_b_c(字段顺序:a→b→c)为基准,覆盖核心生效场景、失效场景及关键补充说明,兼顾实用性与落地性。

1. 核心生效场景(遵循最左前缀法则)

场景类型示例 SQL(where 条件)生效字段核心逻辑
1. 全值等值匹配where a=10 and b=20 and c=30a、b、c(全生效)匹配索引完整字段,引擎逐层精准过滤,效率最优(相当于使用 (a,b,c) 索引)。
2. 最左前缀匹配where a=10a(单独生效)仅使用索引最左字段,符合 “从左开始” 原则(相当于使用 (a) 索引)。
3. 前 N 字段等值匹配where a=10 and b=20a、b(部分生效)匹配索引前 2 个字段,后续字段未使用(相当于使用 (a,b) 索引)。
4. 前 N 字段等值 + 最后字段范围where a=10 and b=20 and c>30a、b、c(全生效)等值字段在前,范围字段在最后,不截断后续索引,全字段生效。
5. 最左字段前缀匹配(模糊查询)where a like '10%'a(单独生效)最左字段尾部模糊匹配(% 在右侧),仅 a 字段索引生效;若开启 ICP,可结合后续字段过滤。
6. 条件顺序无关(优化器自动调整)where b=20 and a=10 and c=30a、b、c(全生效)SQL 条件编写顺序不影响,优化器会按索引字段顺序重排,满足最左前缀即可生效。
7. 覆盖索引查询(无需回表)select a,b,c from t where a=10a、b、c(全生效)查询字段均在索引中,引擎直接从索引取数,无需回表查主表数据。

2. 索引失效 / 部分失效场景

场景类型示例 SQL(where 条件)生效字段失效原因
1. 跳过最左字段where b=20 and c=30无(全失效)违反最左前缀法则,未从索引首字段开始查询,索引完全无法使用。
2. 跳过中间字段where a=10 and c=30a(仅左生效)跳过中间字段 b,后续字段 c 索引失效,仅最左字段 a 生效。
3. 中间字段范围查询where a=10 and b>20 and c=30a、b(部分生效)范围查询(>、<)在中间字段 b,截断后续字段 c 索引,仅 a、b 生效(>=/<= 无此问题)。
4. 最左字段头部 / 全模糊匹配where a like '%10' 或 a like '%10%'无(全失效)% 在最左字段左侧或前后都有,无法匹配索引有序结构,触发全表扫描。
5. 索引字段运算操作where substring (a,1,2)=10 或 a+1=11无(全失效)索引字段直接做函数运算 / 算术运算,引擎无法识别索引结构,索引失效。
6. 字符串字段无引号where a=123(a 为 varchar 类型)无(全失效)类型隐式转换,等价于对 a 做函数运算,破坏索引匹配逻辑。
7. OR 连接条件(单侧无索引)where a=10 or d=40(d 无索引)无(全失效)OR 两侧字段需均有索引,否则有索引的 a 字段也失效,触发全表扫描。
8. 数据分布导致优化器放弃where a is null(a 字段 90% 为 null)无(全失效)优化器评估 “走索引比全表慢”,直接放弃索引(如查询结果占比超 30% 常触发)。

3. 关键补充说明(避坑重点)

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文