← 返回面试专题

🗄️ 慢 SQL / DB 打爆:定位与优化

核心目标:拆解耗时、找瓶颈、做正确的索引与架构改造

🧩 场景题题干(面试官常用表述)

现象:MySQL CPU 飙升,应用接口大量超时。 你如何定位慢 SQL?如何证明是 SQL 问题?如何修复并避免再次发生?
💡 一句话思路:先判断是“DB 扛不住(资源/连接/锁)”还是“SQL 本身慢(执行计划)”,然后用慢日志/EXPLAIN/锁等待证据闭环,最后给出短期止血与长期治理。

📚 学习正文:如何把问题定位到“具体 SQL / 具体索引 / 具体根因”?

交互图:慢 SQL 定位闭环(点步骤看要点与证据)

这张图的目标是把排查过程做成可复述的面试话术:每一步都要能给出“证据”和“下一步动作”,避免只会说“看慢日志、加索引”。

1) 确认慢在 DB(先别急着看 EXPLAIN)

  • 应用侧拆分:把接口总耗时拆出 DB 耗时(日志/APM),证明“慢主要在 DB”。
  • DB 侧指标:CPU/IO、活跃连接、TPS/QPS、锁等待是否异常。
  • 交叉验证:同一条 SQL 直连执行或在只读库执行对比,排除应用线程池/GC 问题。

1) 先止血:控制影响面,避免 DB 被持续打爆

短期目标:让 RT/错误率下降,让 DB 恢复可用 手段(按优先级):限流/降级 → 熔断慢接口 → 摘掉异常实例 → kill 高危语句(谨慎) → 临时扩容/读写分离

2) 证据链第一步:把慢拆成“应用慢”还是“DB 慢”

3) 快速定位:三件套 + 两个补刀

三件套:慢日志/APM → EXPLAIN(ANALYZE) → 压测/回放复现 两个补刀:锁等待分析 → 连接池/线程池耗尽分析

4) 把“慢”归类(不同根因用不同打法)

5) 定位到具体 SQL:从 DB 里把“最贵的”揪出来

-- 1) 看正在执行(立即止血/定位热点)
SHOW FULL PROCESSLIST;

-- 2) MySQL 8:看锁等待/锁对象(需要 performance_schema)
SELECT * FROM performance_schema.data_locks;

-- 3) 慢日志:找到 TopN(示意,实际用 pt-query-digest 更高效)
-- slow_query_log / long_query_time 配置后再分析

6) 定位到具体原因:学会读 EXPLAIN 的“危险信号”

重点关注: type=ALL/index(扫描大) rows 很大(扫描量大) Extra=Using filesort / Using temporary(排序/临时表) key 为空或不符合预期(没命中索引)

7) 常用优化策略(能讲出“为什么有效”)

8) 长期治理:避免“优化一次,下次又炸”

慢 SQL 告警(耗时+次数) 上线前 SQL 评审 + 压测 容量规划(连接池/线程池/实例规格) 数据归档/冷热分离(控制表膨胀)

🎯 面试题(建议学完上面正文再做)

1. 慢的根因通常分哪几类?简单
  • 执行计划问题:未命中索引、全表扫描、回表严重、排序/分组耗时大。
  • 锁等待问题:行锁/间隙锁/元数据锁等待,导致看似“SQL 慢”。
  • 资源瓶颈:CPU/IO/内存不足、buffer pool 不够、磁盘抖动。
  • 连接/线程模型问题:连接数过多、线程上下文切换、连接池打满。
2. 如何快速证明:是 DB 慢导致接口慢?还是应用自身慢?中等
  1. 链路拆分耗时:应用日志/链路追踪把总耗时拆出“DB 耗时”。
  2. DB 侧证据:慢日志、当前运行语句、锁等待、CPU/IO 指标。
  3. 交叉验证:直连 DB 执行同一条 SQL,看响应时间;对比不同时间段差异。
-- 找正在执行的语句(示例,具体看数据库版本/权限)
SHOW FULL PROCESSLIST;

-- 查看 InnoDB 锁等待(MySQL 8 可用 performance_schema 相关表)
SELECT * FROM performance_schema.data_locks;
3. 慢 SQL 定位三件套是什么?中等
  • 慢日志 / APM:拿到“最慢、最频繁、最贵”的 SQL。
  • EXPLAIN:看访问类型、索引、行数估算、Extra。
  • 压测/回放:可复现才能验证优化前后收益。
⚠️ 面试容易翻车点:只会说“加索引”,但解释不清索引为什么有效、为什么没命中、为什么会回表。
4. 你会如何读 EXPLAIN?重点看哪些字段?困难
  • type:从好到坏大致是 system/const/ref/range/index/all。
  • key / possible_keys:最终用了哪个索引,候选有哪些。
  • rows:估算扫描行数,越大越危险(但这是估算)。
  • Extra:Using filesort / Using temporary / Using index / Using where。
💡 面试表达:我会先用 EXPLAIN 判断是不是走了全表扫/临时表/文件排序,再结合数据分布与索引选择解释为什么会慢。
5. DB 被“打爆”时,你有哪些快速止血手段?中等
  • 限流与降级:保护 DB,优先保核心链路。
  • 熔断慢接口:临时下线高耗时接口或降级为读缓存/读备库。
  • kill 高危语句:终止占用资源的慢查询/大事务(谨慎)。
  • 扩容/读写分离:短期用只读副本分担读压力。
⚠️ 注意:kill 语句可能造成事务回滚耗时,甚至更慢;必须评估影响范围。
6. 说一个你最常用的“SQL 优化套路”困难
  1. 缩小数据集:先过滤再 join,避免全量 join。
  2. 对齐索引与 where/order/group:最左前缀、覆盖索引,减少回表。
  3. 避免大 offset:用延迟关联/seek 分页。
  4. 拆分大事务:降低锁持有时间。
  5. 能缓存就缓存:热点读走 Redis/本地缓存。
7. 如何做长期治理,避免“慢 SQL 又回来”?困难
  • 慢 SQL 自动告警:按耗时阈值 + 次数阈值双维度。
  • 变更评审:上线前评审关键 SQL,必要时压测。
  • 容量规划:连接池/线程池/实例规格与 QPS 匹配。
  • 数据生命周期:归档历史数据、冷热分离,避免表无限膨胀。