理解 MySQL 索引的物理结构(B+树)
掌握最左前缀原则、覆盖索引、回表机制
学会索引设计优化常见查询
避免典型的“索引失效”写法
InnoDB 中所有索引,包括聚簇索引和辅助索引,底层都是 B+树 结构。
项目 | B树 | B+树(MySQL用) |
---|---|---|
叶子节点 | 存数据 | 存数据 |
非叶子节点 | 也存数据 | 只存键,不存值 |
查找效率 | 较慢 | 较快(更适合范围查找) |
查询方式 | 可能终止于中间节点 | 必须走到叶子节点 |
所有值都在叶子节点,非叶子节点只用来查路。
联合索引的命中与否,取决于是否满足“最左前缀”原则。
CREATE INDEX idx_name_age ON user(name, age);
可命中索引的写法:
WHERE name = '张三'
WHERE name = '张三' AND age = 20
WHERE name LIKE '张%'
❌ 无法命中索引的写法:
WHERE age = 20
(跳过了 name)
WHERE name LIKE '%三'
(前缀模糊)
联合索引就像电话簿,是“先按姓氏 → 再按名字”顺序排列的。
辅助索引的叶子节点只包含主键值,要查其他列时,需“回表”到聚簇索引查整行。
SELECT name FROM user WHERE email = '[email protected]';
-- 需要回表(索引 idx_email)
如果查询的字段都在索引里,就无需回表。
SELECT email FROM user WHERE email = '[email protected]';
-- 不需要回表(完全命中 idx_email)
回表相当于查目录后翻页找正文,覆盖索引就像直接查目录就拿到答案。
类型 | 说明 |
---|---|
范围后无索引 | WHERE a = 1 AND b > 5 ,b无法命中索引 |
隐式类型转换 | WHERE age = '20' (age 为 INT,右侧为字符串) |
使用函数 | WHERE DATE(create_time) = '2024-01-01' 无法使用索引 |
OR 连多个条件 | WHERE a = 1 OR b = 2 若 b 无索引,则全表扫描 |
前缀模糊匹配 | LIKE '%xxx' 无法使用索引 |
使用区间代替函数:
-- ❌ 索引失效:
WHERE DATE(create_time) = '2024-01-01'
-- ✅ 改写后:
WHERE create_time >= '2024-01-01 00:00:00'
AND create_time < '2024-01-02 00:00:00'
CREATE TABLE employee (
id INT PRIMARY KEY,
name VARCHAR(50),
age INT,
dept_id INT,
INDEX idx_name_age_dept(name, age, dept_id)
);
-- 写法1:命中索引
EXPLAIN SELECT * FROM employee WHERE name = '张三';
-- 写法2:命中索引
EXPLAIN SELECT * FROM employee WHERE name = '张三' AND age = 30;
-- 写法3:无法使用索引
EXPLAIN SELECT * FROM employee WHERE age = 30;
-- 假设 age 是 INT 类型
EXPLAIN SELECT * FROM employee WHERE age = '30';
Extra 字段会出现 Using where
而不是 Using index
CREATE INDEX idx_ab ON t(a, b);
SELECT * FROM t WHERE b = 2 AND a = 1;
答案:顺序错了,写成 a=1 AND b=2 才能命中索引。
答案:观察 EXPLAIN 的 extra
字段,是否包含 Using index
。
答案:LIKE '张%'
可以走索引,LIKE '%三'
无法。
查询字段 → 是否命中索引字段?
↓
按顺序书写? → 是 → 是否前缀模糊? → 否 → ✅ 使用索引
↓ 是 → ❌ 无法使用索引
下一章我们将进入 SQL 执行流程(第4章):
SQL 是怎么一步步执行的?
优化器如何选择执行路径?
EXPLAIN 每列代表什么?如何分析?