MySQL 索引与 SQL 优化
为什么 B+ 树
| 结构 | 问题 |
|---|---|
| 哈希 | 不支持范围、排序 |
| 二叉树 | 可能退化成链,IO 高 |
| B+ 树 | 矮树、叶链表、范围扫描友好 |
InnoDB:主键 = 聚簇索引,叶节点存完整行。
聚簇 vs 二级索引
| 聚簇索引 | 二级索引 | |
|---|---|---|
| 数量 | 每表 1 个 | 可多个 |
| 叶节点 | 完整行 | 主键值 |
| 查询 | — | 通常需 回表 |
覆盖索引:查询列全在索引中 → Using index,无需回表。
最左前缀
联合索引 (a, b, c):
| SQL 条件 | 能否用索引 |
|---|---|
a | ✅ |
a, b | ✅ |
a, b, c | ✅ |
b, c | ❌ 跳过 a |
a, c | ⚠️ 只用到 a |
索引失效(常见)
| 情况 | 原因 |
|---|---|
| 对列 函数/运算 | WHERE YEAR(d)=2024 |
| 隐式类型转换 | 字符串列比数字 |
| 左模糊 | LIKE '%abc' |
| OR 一侧无索引 | 可能全表 |
| 优化器判全表更快 | 小表、选择性差 |
| 不符合最左前缀 | 见上 |
EXPLAIN 看什么
| 列 | 关注 |
|---|---|
| type | ALL 全表差;range/ref/const 较好 |
| key | 实际用的索引 |
| rows | 预估扫描行数 |
| Extra | Using index 覆盖;Using filesort/Using temporary 需优化 |
见 EXPLAIN与运维。
SQL 优化思路
- 慢查询日志 定位 SQL
- EXPLAIN 看是否走索引
- 加/改索引(覆盖、最左、选择性高的列在前)
- 避免
SELECT * - 深分页:
WHERE id > lastId LIMIT n代替大 offset - 批量写、减少锁范围
主键选型
| 自增整型 | UUID | |
|---|---|---|
| 插入 | 顺序追加,少页分裂 | 随机,分裂多 |
| 推荐 | InnoDB 首选 | 分布式 ID 可用 Snowflake |
常见面试题
Q:索引是不是越多越好?
A:否;写要维护索引,占空间;按查询路径建,删除冗余索引。
Q:唯一索引 vs 普通索引?
A:唯一索引插入需判重,略慢;都能加速查询。
Q:前缀索引?
A:INDEX(name(10)) 省空间;无法覆盖排序/Group By 全列。
Q:慢 SQL 怎么排查?
A:slow_query_log → EXPLAIN → 索引/改写 SQL → 验证 rows 与耗时。