一、B+Tree 与磁盘 I/O
- B+Tree 每次向下查找一层,都会产生一次 磁盘 I/O
- 树的高度越低,I/O 次数越少,查询越快
二、聚簇索引(Clustered Index)
InnoDB 中,主键索引就是聚簇索引
聚簇索引结构
id
↓
完整的一行数据
- 叶子节点直接存储 整行数据
- 一张表 只有一个 聚簇索引(通常是主键)
三、二级索引(Secondary Index)
- 叶子节点存储的是:主键值,而非整行数据
回表(Bookmark Lookup)
二级索引 → 主键 id → 聚簇索引 → 整行数据
- 使用二级索引查非索引列时,会发生 回表
- 如果只查询 二级索引列 + 主键,则 不需要回表
覆盖索引(Covering Index)
- 查询的列 全部包含在索引中
- Extra 中显示:
Using index - ✅ 性能最好,避免回表
四、EXPLAIN 解读
| 字段 | 含义 | 如何判断优劣 |
|---|---|---|
| type | 访问类型(查询方式) | 越靠近 const 越好ref:普通索引range:索引范围扫描(如 BETWEEN、>)❌ 避免 ALL(全表扫描) |
| key | 实际使用的索引 | 不能为 NULL |
| rows | 预估扫描行数 | 越小越好 |
| Extra | 额外信息 | ✅ Using index(覆盖索引)⚠️ Using filesort⚠️ Using temporary |
EXPLAIN 阅读顺序(口诀)
- 看
type:有没有全表扫描 - 看
key:是否真正用到索引 - 看
rows:扫描了多少行 - 看
Extra:是否有排序、临时表、覆盖索引
五、SQL 慢查询排查流程
SQL 很慢
│
▼
① 有没有走索引?
│
▼
② 为什么没走索引?
│
▼
③ 扫描了多少行?
│
▼
④ 有没有排序?
│
▼
⑤ 有没有回表?
六、常见导致索引失效的原因
- 隐式类型转换
- 联合索引违反 最左匹配原则
- 对索引列做函数操作
OR条件不当LIKE '%xxx'(前模糊)
七、ORDER BY 为什么会慢?
ORDER BY age且age无索引- MySQL 无法利用索引排序
- 结果:
Using filesort - 本质:把数据查出来后再在内存 / 磁盘中排序
八、事务隔离级别与 MVCC(快照)
- RC(Read Committed):每次 SELECT 都生成新快照
- RR(Repeatable Read):事务内使用同一快照(MySQL InnoDB 默认)
本质:MVCC(多版本并发控制)
九、深分页问题(LIMIT 优化)
SELECT * FROM t LIMIT 1000000, 10; -- ❌ 很慢
原因
- MySQL 需要先扫描前 1000000 条记录
- 再丢弃,只取 10 条
优化方案:游标分页(Keyset Pagination)
SELECT * FROM t WHERE id > 1000000 ORDER BY id LIMIT 10;
✅ 利用索引顺序定位
✅ 避免 OFFSET 扫描
✅ 性能稳定,适合大数据量翻页
索引决定 I/O,EXPLAIN 决定方向,回表和排序决定代价,分页和隔离级别决定边界。
评论
填写昵称与邮箱即可评论,无需登录。