MySQL 索引 → 慢SQL → 优化实战 追问链#
追问路径#
Q: MySQL索引为什么用B+树而不是B树或Hash?
→ B+树矮胖减少磁盘IO(3层可存约2000万行),叶子节点链表支持范围查询;Hash不支持范围和排序
Q: 聚簇索引和二级索引有什么区别?
→ 聚簇索引叶子节点存完整数据行(InnoDB主键索引);二级索引叶子存主键值,查完整数据需要回表
Q: 什么是回表?怎么避免?
→ 二级索引查到主键后再回聚簇索引取数据行;用覆盖索引(select的列都在索引中)避免
├─ Q: 联合索引怎么设计?
│ → 最左前缀原则;区分度高的列放前面;考虑覆盖索引减少回表
│ Q: 索引失效的常见场景?
│ → 对索引列使用函数/隐式类型转换/like左模糊/%开头/or连接非索引列/不满足最左前缀
│ Q: explain怎么看?哪些字段最重要?
│ → type(至少range)/key(实际使用的索引)/rows(扫描行数)/Extra(Using index=覆盖索引, Using filesort=额外排序)
│ Q: 生产环境慢SQL怎么优化?
│ → 开启慢日志(long_query_time=1s) → explain分析 → 加索引/改SQL/改业务 → 压测验证
└─ Q: 深分页怎么优化?
→ LIMIT 100000, 10性能差——扫描100010行丢弃前100000行
Q: 有哪些方案?
→ 延迟关联(子查询先取主键再join)/游标分页(where id > last_id limit 10)/覆盖索引子查询
Q: 大表加索引怎么不锁表?
→ pt-online-schema-change/gh-ost(无触发器); MySQL 8.0 instant DDL(部分操作)plaintext涉及知识点#
- B+树索引原理 — 为什么B+树适合磁盘存储
- 聚簇索引与非聚簇索引 — InnoDB索引存储方式
- 覆盖索引与回表 — 减少IO的核心优化手段
- 最左前缀原则 — 联合索引的匹配规则
- 联合索引设计原则 — 区分度、覆盖、排序三要素
- 索引失效场景 — 8大索引失效原因
- 索引下推ICP — MySQL 5.6的索引优化
- explain执行计划 — SQL调优的核心工具
- 慢查询优化实战 — 从定位到修复的完整流程
- 大表分页优化 — 深分页的3种解决方案
- 大表DDL操作实战 — 在线DDL工具对比
核心串联逻辑#
- B+树优势:3层B+树(16KB页, bigint主键)可存 ~2000万行,只需3次磁盘IO
- 回表代价:每次回表是一次随机IO,覆盖索引将随机IO变为顺序IO
- 索引设计:遵循 ESR 原则(Equal → Sort → Range)设计联合索引
- 慢SQL排查链:
slow_query_log→explain→ 索引优化 → SQL改写 → 压测验证 - 深分页本质:
LIMIT offset, size会扫描offset+size行然后丢弃前offset行 - 代码示例:
sql-- 延迟关联优化深分页 SELECT * FROM orders o INNER JOIN (SELECT id FROM orders ORDER BY create_time LIMIT 100000, 10) t ON o.id = t.id;
面试回答串联#
30秒速答#
“B+树3层可存2000万行,叶子链表支持范围查询。二级索引查完需要回表取数据,用覆盖索引可以避免。联合索引遵循最左前缀原则,函数、隐式转换、like左模糊会导致索引失效。慢SQL用explain的type和rows字段定位瓶颈。“
2分钟展开答#
“MySQL用B+树索引是因为矮胖结构减少磁盘IO——3层约存2000万行,只需3次IO。InnoDB的聚簇索引叶子存完整数据行,二级索引叶子存主键值,所以二级索引查询后需要回表再取数据。覆盖索引让查询列都在索引中,把随机IO变顺序IO。联合索引设计遵循ESR原则:等值条件放前面,排序字段放中间,范围条件放最后。索引失效常见于对列用函数、varchar和int比较导致隐式转换、like左模糊等场景。生产中我的慢SQL排查流程是:先开慢日志定位(long_query_time=1s),explain看type至少要到range、检查rows扫描行数、Extra中的Using filesort和Using temporary,然后针对性加索引或改写SQL。深分页用延迟关联——子查询先走覆盖索引取主键,再join回原表取数据。大表DDL用gh-ost无触发器方案避免锁表。“
相关追问链#
- MySQL事务-分布式事务-CAP追问链 — 索引与锁的交互(行锁通过索引实现,无索引则退化为表锁)