02.慢查询与执行计划
文章目录
问题: 请说明定位和优化慢SQL的完整思路;在解读EXPLAIN执行计划时,如何根据type(如eq_ref、range、index、ALL)、extra(如Using filesort、Using temporary)和rows来精准定位性能瓶颈?
👇 点击展开/折叠 面试背诵模板(建议先背这个)
一、定位和优化慢SQL的完整思路
① 开启慢查询日志,抓取问题SQL
- 设置
slow_query_log = ON,定义阈值long_query_time(如1秒)。 - 定期分析慢查询日志(
mysqldumpslow工具或系统表slow_log),按执行次数和耗时排序,优先优化TOP N。
② 用EXPLAIN分析执行计划
- 对抓取到的慢SQL执行
EXPLAIN [SQL],获取MySQL优化器选择的具体执行路径。
③ 根据执行计划定位瓶颈
- 看
type:判断扫描效率(ALL/index最差,range/ref可接受)。 - 看
extra:识别额外操作(Using filesort/Using temporary为明确优化信号)。 - 看
rows:估算扫描行数,行数越大,性能风险越高。 - 看
key/possible_keys:判断索引是否被正确使用。
④ 优化策略迭代
- 建立/调整索引(覆盖索引、联合索引)。
- 改写SQL(拆分为多条简单查询、避免函数运算、用exists替代in)。
- 调整表结构(拆分大表、优化字段类型)。
- 调整数据库参数(如join_buffer_size、sort_buffer_size)。
⑤ 验证效果并闭环
- 在测试环境复现并对比优化前后的
EXPLAIN和实际执行时间。 - 上线后持续监控,形成优化闭环。
二、EXPLAIN关键字段解读与瓶颈定位
① type(连接类型,性能排序从好到差)
| type值 | 含义 | 优化建议 |
|---|---|---|
| system | 表中只有一行记录(系统表),常数级 | 无需优化 |
| const | 主键或唯一索引等值匹配,最多返回一行 | 理想状态 |
| eq_ref | 唯一索引连接(join),对驱动表每行,被驱动表最多返回一行 | 优秀的join效率 |
| ref | 非唯一索引等值匹配,返回多行 | 可接受,需注意扫描范围 |
| range | 索引范围扫描(BETWEEN、>、<、IN) | 目标最低要求,通常可接受 |
| index | 覆盖索引扫描(索引全扫描,未回表) | 可优化,若rows过大需关注 |
| ALL | 全表扫描(未使用索引) | 必须优化,建立索引或改写SQL |
优化目标:type 至少达到 range,核心业务查询争取达到 ref 或 eq_ref。
② extra(额外信息,关注三种危险信号)
| extra值 | 含义 | 优化方向 |
|---|---|---|
| Using filesort | 需在内存/磁盘进行额外排序,索引无法直接提供有序数据 | 对排序字段建立联合索引(让索引天然有序) |
| Using temporary | 需创建临时表来存储中间结果(常见于group by、distinct、union) | 优化group by/order by顺序,建立复合索引消除临时表 |
| Using index | 使用了覆盖索引,数据直接从索引获取,无需回表 | 理想状态,保持即可 |
| Using where | 索引定位后还需回表过滤 | 可结合覆盖索引优化,或放宽索引条件 |
优化原则:EXPLAIN 中若出现 Using filesort 或 Using temporary,优先消除这两种操作,通常优化效果最显著。
③ rows(估算扫描行数)
rows是优化器基于索引统计信息估算的需要扫描的行数,非精确值。- 数值越大,意味着查询需要访问的数据量越大,性能越差。
- 优化目标:通过
WHERE条件和索引设计,将rows降至尽可能小(如从10万降到100)。
④ key / possible_keys
possible_keys列出可供选择的索引,key为实际使用的索引。- 若
key为NULL,说明未命中索引,大概率type = ALL。 - 若
possible_keys有多个,但key选择非最优(如选择了一个选择性差的索引),可考虑使用FORCE INDEX强制使用更优索引。
三、实战优化口诀与核心原则
- **优先消除
Using filesort和Using temporary**:这两者往往是慢查询的根本原因,比type=ALL更隐蔽但也更影响性能。 - 让索引匹配到
range及以上:建立联合索引时,根据查询条件顺序调整索引字段顺序(遵循最左匹配原则),避免ALL全表扫描。 - 用
rows× 单行数据大小 估算内存/磁盘I/O:rows并非唯一指标,还需结合extra判断是否回表、是否排序,综合判断性能瓶颈。 - 覆盖索引优先:若查询字段全部包含在索引中,可直接通过索引返回数据,无需回表,效率最高。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 LGQ的博客!