MySQL 专题(二):索引原理与优化
MySQL 专题(二):索引原理与优化
在 MySQL 性能优化中,索引 是最核心的工具之一。它就像一本书的目录,可以帮我们快速定位数据,而不用一页一页去翻。
今天这篇文章我们会分两部分:
- 索引的底层原理(B+ 树)
- SQL 优化与索引的正确使用
一、B+ 树:MySQL 索引的底层原理
在 MySQL 的 InnoDB 存储引擎中,索引底层结构是 B+ 树。
1. B+ 树结构示意
假设我们在 user(id, name, age)
表的 id
字段上建立了主键索引,B+ 树结构大致如下:
[100 | 500 | 900] ← 根节点(索引目录)/ | \[1..99] [100..499] [500..899] [900..∞] ← 中间节点| | | |┌─────────┴─────────┘ | |↓ ↓ ↓
[id=10,data] - [id=50,data] ... [id=499,data] ... [id=899,data] ... [id=1200,data]↑───────────────────────────────↑───────────────────────────────↑───────────────叶子节点(存储整行数据) 叶子节点之间通过链表连接(范围查询超快)
👉 特点:
- 根节点 & 中间节点 只存“目录 key”,不存实际数据。
- 所有数据都在叶子节点。
- 叶子节点之间有链表,支持范围扫描 (
BETWEEN
,ORDER BY
)。
2. 查询过程示例
-
查询
id = 500
- 根节点定位
[500..899]
区间 - 进入中间节点找到
500
- 进入叶子节点,取整行数据
👉 只需 3 次磁盘 IO。
- 根节点定位
-
范围查询
200 ≤ id ≤ 600
- 定位到
200
的叶子节点 - 沿着叶子链表顺序扫描到
600
👉 效率极高,比普通 B 树快。
- 定位到
3. 为什么选择 B+ 树而不是哈希索引或二叉树?
-
B+ 树 vs 二叉树
- 二叉树深度大,磁盘 IO 次数多;
- B+ 树扇出大(每个节点能容纳很多 key),层数更少,查询更快。
-
B+ 树 vs Hash 索引
- 哈希索引只适合等值查询(
=
),不支持范围查询; - B+ 树既支持等值查找,也支持范围查找。
- 哈希索引只适合等值查询(
👉 总结:B+ 树是 MySQL 索引的最佳平衡点。
二、SQL 优化与索引使用
理解了 B+ 树原理后,我们就能更好地写“索引友好”的 SQL。否则,即使建了索引,也可能失效。
1. 索引失效的常见场景
- (1) 对索引列做运算
SELECT * FROM user WHERE age + 1 = 30;
❌ 失效(age+1 破坏索引)。
✅ 改写:
SELECT * FROM user WHERE age = 29;
- (2) 使用函数处理索引列
SELECT * FROM user WHERE DATE(create_time) = '2025-09-12';
❌ 失效。
✅ 改写范围:
SELECT * FROM user
WHERE create_time >= '2025-09-12 00:00:00'AND create_time < '2025-09-13 00:00:00';
- (3) 模糊查询前缀
%
SELECT * FROM user WHERE name LIKE '%Tom';
❌ 前缀 %
导致全表扫描。
✅ 优化:
SELECT * FROM user WHERE name LIKE 'Tom%';
或使用全文索引。
- (4) OR 条件
SELECT * FROM user WHERE id=10 OR age=20;
❌ 索引失效。
✅ 改写:
SELECT * FROM user WHERE id=10
UNION
SELECT * FROM user WHERE age=20;
- (5) 类型不匹配
SELECT * FROM user WHERE phone = 13888888888;
❌ phone 是字符串,但没加引号 → 索引失效。
✅ 改写:
SELECT * FROM user WHERE phone = '13888888888';
2. 覆盖索引(Covering Index)
如果查询只涉及索引列,就不需要“回表”。
- 覆盖索引查询:
SELECT id, name FROM user WHERE id=100;
👉 只查索引,不访问数据页,性能极高。
- 非覆盖索引查询:
SELECT id, name, age FROM user WHERE id=100;
👉 需要回表取 age
,多一次 IO。
3. 最左前缀原则
联合索引 (a, b, c)
的使用规则:
- ✅
a
、a,b
、a,b,c
- ❌ 单独用
b
或c
SELECT * FROM user WHERE b=10; -- 索引无效
👉 建索引时要根据 查询习惯,把过滤最常用、区分度最高的列放在前面。
4. 索引选择性
索引的效果取决于 区分度(不同值的数量)。
- 性别
gender
只有男/女 → 区分度低,不适合建索引。 - 身份证号
id_number
唯一值很多 → 区分度高,适合建索引。
三、总结
- MySQL 索引的底层是 B+ 树,数据存放在叶子节点,叶子之间通过链表连接,非常适合范围查询。
- 索引的高效利用依赖 写法:避免函数、运算、前缀
%
、OR、类型转换。 - 覆盖索引 和 最左前缀原则 是写高性能 SQL 的关键。
- 索引要建在 区分度高的列 上,才能真正提升性能。
👉 一句话总结:
理解 B+ 树原理,写出索引友好的 SQL,你的 MySQL 性能优化就成功了一大半。