MySQL 表 t1 建立联合索引 (a, b, c),在 where a < ? and b > ? and c < ? 中哪些索引生效

文章目录

  • [联合索引 abc 均范围扫描时的索引生效情况](#联合索引 abc 均范围扫描时的索引生效情况)
    • [无回表 + 表数据量非常少](#无回表 + 表数据量非常少)
    • [无回表 + 表数据量多](#无回表 + 表数据量多)
    • 有回表
    • 总结

联合索引 abc 均范围扫描时的索引生效情况

场景:表 t1 建立联合索引 (a, b, c),在 where a < ? and b > ? and c < ? 中哪些索引生效。

无回表 + 表数据量非常少

场景准备:联合索引 (a, b, c) 已经是完整的数据记录,可以使用覆盖索引,表数据量非常少的意思是只有 0 或 1 条记录(测试发现)。

sql 复制代码
DROP TABLE IF EXISTS t1;

CREATE TABLE t1 (
	id INT AUTO_INCREMENT PRIMARY KEY,
	a INT NOT NULL,
	b INT NOT NULL,
	c INT NOT NULL,
	INDEX idx_a_b_c(a, b, c)
);

drop procedure if exists GenerateTestData;

DELIMITER //

CREATE PROCEDURE GenerateTestData()
BEGIN
    DECLARE i INT DEFAULT 0;
    
    WHILE i < 1 DO
        INSERT INTO t1 (a, b, c)
        VALUES (
            FLOOR(1 + RAND() * 100),  -- 生成 1-100 的随机整数
            FLOOR(1 + RAND() * 100),
            FLOOR(1 + RAND() * 100)
        );
        SET i = i + 1;
    END WHILE;
END //

DELIMITER ;

call GenerateTestData;

查看当前表信息:

sql 复制代码
SELECT * FROM t1;

接着分析此场景下的联合索引生效情况:

sql 复制代码
EXPLAIN
SELECT *
FROM t1
WHERE a < 2 AND b > 3 AND c < 5; 

分析:

  • type = index 表示可以使用覆盖索引,但需要扫描全部的索引记录。
  • key = idx_a_b_c 表示实际用到的索引为联合索引 (a, b, c)。
  • key_len = 12 表示实际用到的索引长度(字节数)为 12,这是衡量联合索引字段生效的重要参考。
  • Extra = Using where 表示在 Server 层进行了条件过滤。
  • Extra = Using index 表示使用到了覆盖索引。

🤔 为什么这个 where 条件明明不满足最左前缀原则,key_len 的长度还为 12 呢?

首先说明的是,a、b、c 都是 4 字节的 int 类型,因此 索引字段数 × 字段长度 = 3 × 4 = 12 = k e y _ l e n 索引字段数 \times 字段长度 = 3 \times 4 = 12 = key\_len 索引字段数×字段长度=3×4=12=key_len 说明实际用到了 a、b、c。但这并不表明 a、b、c 索引生效!因为 type = index 表示优化器选择了全索引扫描(遍历整个索引),所以才呈现了 key_len = 12 的情况。

也就是说,a、b、c 没有一个索引生效,即没有在存储引擎层利用索引进行条件过滤,实际的条件过滤是由 Server 层进行的。

为了进一步验证,还可以使用 EXPLAIN ANALYZEEXPLAIN ANALYZE 是 MySQL 8.0.18 及以上版本引入的一个调试工具,用于分析 SQL 查询的实际执行过程。它不仅显示优化器预估的执行计划 (类似常规的 EXPLAIN),还会实际执行查询并返回详细的运行时统计信息(如实际耗时、处理行数等),帮助开发者精准定位性能瓶颈。

sql 复制代码
EXPLAIN analyze
SELECT *
FROM t1
WHERE a < 2 AND b > 3 AND c < 5;

分析:

  1. Covering 表示使用到了覆盖索引,需要查询的所有字段都在索引搜索树 (a, b, c) 中可以找到,无需回表查询。
  2. index scan 表示进行了全索引扫描。
  3. Filter: ((t1.a < 2) and (t1.b > 3) and (t1.c < 5)) 表示存储引擎层在全索引扫描后,Server 层将结果集在内存中按照 a < 2 AND b > 3 AND c < 5 进行条件过滤。

无回表 + 表数据量多

场景准备:联合索引 (a, b, c) 已经是完整的数据记录,可以使用覆盖索引,表数据量多的意思是多于 1 条记录(测试发现)。

sql 复制代码
DROP TABLE IF EXISTS t1;

CREATE TABLE t1 (
	id INT AUTO_INCREMENT PRIMARY KEY,
	a INT NOT NULL,
	b INT NOT NULL,
	c INT NOT NULL,
	INDEX idx_a_b_c(a, b, c)
);

drop procedure if exists GenerateTestData;

DELIMITER //

CREATE PROCEDURE GenerateTestData()
BEGIN
    DECLARE i INT DEFAULT 0;
    
    WHILE i < 1000 DO
        INSERT INTO t1 (a, b, c)
        VALUES (
            FLOOR(1 + RAND() * 100),  -- 生成 1-100 的随机整数
            FLOOR(1 + RAND() * 100),
            FLOOR(1 + RAND() * 100)
        );
        SET i = i + 1;
    END WHILE;
END //

DELIMITER ;

call GenerateTestData;

接着分析此场景下的联合索引生效情况:

sql 复制代码
EXPLAIN
SELECT *
FROM t1
WHERE a < 2 AND b > 3 AND c < 5; 

分析:

  • type = range 表示使用索引进行范围查找。
  • key = idx_a_b_c 表示实际用到的索引为联合索引 (a, b, c)。
  • key_len = 4 表示实际用到的索引长度(字节数)为 4 ,也就是只有 a 字段索引生效
  • Extra = Using where 表示在 Server 层进行了条件过滤。
  • Extra = Using index 表示使用到了覆盖索引。

由于进行了范围查找,不满足最左前缀原则,因此只有 a 字段索引生效,后续的 b、c 都未生效,并在 Server 层进行 a、b、c 的条件过滤。

🤔 Server 层为什么还会对 a 进行过滤呢,存储引擎层不是已经过滤了 a 吗?

这是因为存储引擎对 Server 层是"透明"的,Server 层不假设存储引擎的行为完全可靠,因此会重新验证数据

为了进一步验证,还可以使用 EXPLAIN ANALYZE

sql 复制代码
EXPLAIN analyze
SELECT *
FROM t1
WHERE a < 2 AND b > 3 AND c < 5;

分析:

  1. Covering 表示使用到了覆盖索引,需要查询的所有字段都在索引搜索树 (a, b, c) 中可以找到,无需回表查询。
  2. range scan ... over (a < 2) 表示对 a < 2 进行了范围扫描,仅 a 字段索引生效,b、c 未生效。
  3. Filter: ((t1.a < 2) and (t1.b > 3) and (t1.c < 5)) 表示存储引擎层在范围扫描后,Server 层将结果集在内存中按照 a < 2 AND b > 3 AND c < 5 进行条件过滤。

有回表

场景准备:联合索引 (a, b, c) 不是完整的数据记录,需要回表扫描,这里不强调表数据量大小的原因是无论数量为 0、1 或更多都会回表扫描(测试发现)。

sql 复制代码
DROP TABLE IF EXISTS t1;

CREATE TABLE t1 (
	id INT AUTO_INCREMENT PRIMARY KEY,
	`name` VARCHAR(255) NOT NULL DEFAULT '',
	a INT NOT NULL,
	b INT NOT NULL,
	c INT NOT NULL,
	INDEX idx_a_b_c(a, b, c)
);

DROP PROCEDURE IF EXISTS GenerateTestData;

DELIMITER //

CREATE PROCEDURE GenerateTestData()
BEGIN
    DECLARE i INT DEFAULT 0;
    
    WHILE i < 1000 DO
        INSERT INTO t1 (a, b, c)
        VALUES (
            FLOOR(1 + RAND() * 100),  -- 生成 1-100 的随机整数
            FLOOR(1 + RAND() * 100),
            FLOOR(1 + RAND() * 100)
        );
        SET i = i + 1;
    END WHILE;
END //

DELIMITER ;

CALL GenerateTestData;

接着分析此场景下的联合索引生效情况:

sql 复制代码
EXPLAIN
SELECT *
FROM t1
WHERE a < 2 AND b > 3 AND c < 5; 

分析:

  • type = range 表示使用索引进行范围查找。
  • key = idx_a_b_c 表示实际用到的索引为联合索引 (a, b, c)。
  • key_len = 4 表示实际用到的索引长度(字节数)为 4 ,也就是只有 a 字段索引生效
  • Extra = Using index condition 表示在存储引擎层使用 b、c 进行了索引下推。

由于进行了范围查找,不满足最左前缀原则,因此只有 a 字段索引生效,后续的 b、c 都未生效,但由于需要回表查询,因此还可以使用 b、c 进行索引下推,且在 Server 层不会再进行条件过滤了(因为没有提示 Extra = Using where)。

为了进一步验证,还可以使用 EXPLAIN ANALYZE

sql 复制代码
EXPLAIN analyze
SELECT *
FROM t1
WHERE a < 2 AND b > 3 AND c < 5;

分析:

  1. range scan ... over (a < 2) 表示对 a < 2 进行了范围扫描,仅 a 字段索引生效,b、c 未生效。
  2. index condition: (a < 2 and b > 3 and c < 5) 表示在索引范围扫描的基础上,存储引擎进一步应用索引下推,检查索引条目是否满足 a < 2b > 3c < 5 再进行回表扫描。

🤔 为什么生效的索引字段 a 还会作为索引下推条件呢?

虽然 a < 2 已用于范围扫描,但 ICP 仍会重新检查所有下推条件。也就是说,ICP 的条件列表中可能包含了已经被范围扫描处理的条件,这是因为在索引扫描的过程中,存储引擎可能需要再次确认这些条件,尤其是在联合索引中,可能存在多个范围的条目,需要逐条检查。

总结

这里只分析了目前能想到的常见情况,其实还有很多,这是由于查询优化器会对查询进行优化,包括重写查询、决定表的读写顺序、选择合适的索引等,综合考虑数据量、是否回表、回表成本、索引区分度等因素生成查询成本最小的执行计划。

对以上的三种情况做一个总结:

  1. 无回表 + 表数据量非常少:使用覆盖索引进行全索引扫描,索引字段 a、b、c 都未生效(未使用索引进行条件过滤)。
  2. 无回表 + 表数据量多:使用联合索引进行范围查找,但只有索引字段 a 生效,Server 层使用 a、b、c 进行条件过滤(尽管存储引擎层已经过滤了 a,但 Server 层不认为存储引擎层行为完全可靠)。
  3. 有回表:使用联合索引进行范围查找,但只有索引字段 a 生效,存储引擎层使用 a、b、c 进行索引下推(尽管大多数 ICP 场景只有 b、c 才能真正过滤掉部分数据)。
相关推荐
Databend3 小时前
Jev 爆火之后,我们把它集成进了数据湖仓
大数据·数据库·agent
数字新视界3 小时前
数据中心基础设施管理系统的智能监测与运维整合剖析
数据库·物联网·数据中心·数据中心基础设施管理·dcim管理系统
于樱花森上飞舞3 小时前
【Redis】哨兵详解
java·开发语言·数据库·redis
徐子童4 小时前
介绍MVCC机制
java·mysql·面试题·秋招·并发·mvcc
thefool1122664 小时前
库和表基本操作、表的增删改查
mysql
李白客4 小时前
向量数据库怎么选:RAG 项目最该比较的 8 个维度(深度拆解)
数据库·aigc
cspttty4 小时前
需求预测校招入门:SQL、Excel、统计模型和业务指标怎么学
大数据·数据库
2501_933670794 小时前
面向物流数据分析校招:SQL、Excel、BI、WMS/TMS该怎么准备
数据库
Code_流苏4 小时前
AI 知识库和数据库有什么区别?从“查订单”到“问规则”讲清楚
数据库·ai·agent·知识库
YangYang9YangYan4 小时前
财务 BP 校招面试案例整理|业务场景题 + 数据案例,2027 届求职
大数据·数据库