MySQL: MySQL索引进阶与EXPLAIN执行计划

最后更新:2026-08-26

理解索引的高级特性,才能真正写出高性能查询。

本课深入讲解索引类型和 EXPLAIN 分析。

1. 你将学到


2. 聚簇索引与非聚簇索引

维度 聚簇索引 非聚簇索引
存储方式 数据和索引在一起 索引指向数据地址
数量 只能一个 可以多个
主键 InnoDB 自动用主键 二级索引
查询 直接获取数据 需要回表

(1) 回表查询

100%
graph LR
    A[二级索引<br/>idx_email] -->|主键值| B[聚簇索引<br/>PRIMARY]
    B -->|完整数据| C[数据行]
SQL
-- 回表过程
SELECT * FROM users WHERE email = 'alice@email.com';
-- 1. 在 idx_email 找到 id=1
-- 2. 在 PRIMARY 找到 id=1 的完整数据

3. 覆盖索引

查询的字段都在索引中,无需回表。

▶ 示例:覆盖索引

SQL
-- 创建联合索引
CREATE INDEX idx_name_email ON users(username, email);

-- 覆盖索引(无需回表)
SELECT username, email FROM users WHERE username = 'alice';

-- 非覆盖索引(需要回表)
SELECT * FROM users WHERE username = 'alice';
▶ 试一试
类型 是否回表 性能
覆盖索引
非覆盖索引

4. 联合索引与最左前缀

(1) 最左前缀原则

联合索引 (a, b, c) 可以被以下查询使用:

SQL
WHERE a = 1                    -- ✅ 使用索引
WHERE a = 1 AND b = 2         -- ✅ 使用索引
WHERE a = 1 AND b = 2 AND c = 3  -- ✅ 使用索引
WHERE b = 2                    -- ❌ 未使用索引
WHERE b = 2 AND c = 3         -- ❌ 未使用索引

▶ 示例:索引使用验证

SQL
CREATE INDEX idx_abc ON orders(customer_id, status, order_date);

-- ✅ 使用索引
EXPLAIN SELECT * FROM orders WHERE customer_id = 1;
EXPLAIN SELECT * FROM orders WHERE customer_id = 1 AND status = 'paid';
EXPLAIN SELECT * FROM orders WHERE customer_id = 1 AND status = 'paid' AND order_date > '2026-01-01';

-- ❌ 未使用索引(违反最左前缀)
EXPLAIN SELECT * FROM orders WHERE status = 'paid';
▶ 试一试

5. 索引失效场景

场景 示例 原因
函数操作 WHERE YEAR(date) = 2026 索引被函数包裹
隐式转换 WHERE phone = 13800001111 字符串列用数字查询
LIKE 左模糊 WHERE name LIKE '%john' 前缀不确定
OR 条件 WHERE a = 1 OR b = 2 部分字段无索引
NOT IN/NOT EXISTS WHERE id NOT IN (1,2) 优化器选择全表扫描
IS NULL/IS NOT NULL WHERE col IS NULL 取决于 NULL 比例

▶ 示例:索引失效与修复

SQL
-- ❌ 索引失效
SELECT * FROM users WHERE YEAR(created_at) = 2026;

-- ✅ 修复:改为范围查询
SELECT * FROM users WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01';

-- ❌ 索引失效
SELECT * FROM users WHERE phone = 13800001111;

-- ✅ 修复:加引号
SELECT * FROM users WHERE phone = '13800001111';
▶ 试一试

6. EXPLAIN 执行计划

▶ 示例:EXPLAIN 用法

SQL
EXPLAIN SELECT * FROM users WHERE email = 'alice@email.com';
▶ 试一试

输出:

TEXT 📖 仅展示
+----+-------+------+---------+------+----------+-------+
| id | type  | key  | key_len | ref  | rows     | Extra |
+----+-------+------+---------+------+----------+-------+
|  1 | const | idx_email | 402 | const | 1 |       |
+----+-------+------+---------+------+----------+-------+

(1) 关键字段说明

字段 说明 好值
type 访问类型 const > eq_ref > ref > range > index > ALL
key 使用的索引 不为 NULL
rows 预估扫描行数 越小越好
Extra 额外信息 Using index(覆盖索引)

(2) type 访问类型

类型 说明 优劣
const 主键/唯一索引等值查询 ⭐⭐⭐
eq_ref 连接时使用主键/唯一索引 ⭐⭐⭐
ref 非唯一索引等值查询 ⭐⭐
range 索引范围查询 ⭐⭐
index 全索引扫描
ALL 全表扫描

❓ 常见问题

Q 联合索引的字段顺序怎么选?
A 选择性高的放前面,查询频率高的放前面。
Q EXPLAIN 的 rows 准确吗?
A 是估算值,不是精确值。但可以用来判断查询效率。
Q 索引失效怎么办?
A 改写 SQL 避免索引失效场景,或创建函数索引(MySQL 8.0+)。
Q EXPLAIN 的 rows 准确吗?
A 是估算值,不是精确值,但可对比相对变化判断优化效果。用 ANALYZE TABLE 更新统计信息。
Q 什么时候用前缀索引?
A VARCHAR 字段很长且前缀区分度够高时。如 INDEX(email(10)),前 10 字符区分度>95%即可。

📖 小节


📝 作业

  1. 基础题(难度⭐):用 EXPLAIN 分析一个查询,确认是否使用了索引。

  2. 进阶题(难度⭐⭐):创建联合索引,验证最左前缀原则。

  3. 挑战题(难度⭐⭐⭐):找出 3 个索引失效的场景并给出修复方案。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏