八股文解析
线上慢查询怎么排查和优化?
一句话结论
先抓慢查询日志定位SQL,再用EXPLAIN看执行计划,最后按索引→锁→CPU/IO→架构的顺序逐层优化。
面试标准答法
第一层:定位——先找到“罪犯”
线上慢查询排查的第一步不是优化,而是发现。MySQL提供了两个核心工具:
- slow_query_log:记录执行时间超过
long_query_time(默认10s,线上建议设为1s甚至0.5s)的SQL。关键参数: slow_query_log_file:日志文件路径log_queries_not_using_indexes:记录未走索引的查询(建议开启,这是隐性慢查询的捕获器)- performance_schema:MySQL 5.7+ 内置的监控库,
events_statements_summary_by_digest表可以按SQL模板聚合统计,直接找出总耗时Top N的SQL,比翻日志更高效。
第二层:分析——用EXPLAIN拆解执行计划
EXPLAIN是慢查询优化的核心工具,但90%的人只会看 type 和 rows,这是不够的。必须看全以下字段:
| 字段 | 含义 | 红线标准 |
|---|---|---|
| type | 访问类型,从好到差:system > const > eq_ref > ref > range > index > ALL | 出现ALL(全表扫描)必须优化 |
| key | 实际使用的索引名 | 为NULL说明没走索引 |
| rows | 预估扫描行数 | 超过表行数10%就该警惕 |
| Extra | 附加信息 | 出现 Using filesort、Using temporary 是性能杀手 |
最关键的判断逻辑:
- type=ALL → 索引缺失或索引失效。检查WHERE条件、JOIN字段、ORDER BY字段是否有索引。
- type=index → 索引全扫描,通常是因为覆盖索引列顺序不对,或SELECT了索引外的列。
- Extra有Using filesort → ORDER BY字段没走索引,MySQL需要额外排序,数据量大时极慢。
- Extra有Using temporary → GROUP BY或DISTINCT导致临时表,常见于多表JOIN + 分组。
第三层:优化——按优先级逐层击破
3.1 索引优化(最高性价比)
- 单列索引:WHERE条件中高频字段建索引
- 复合索引:遵循最左前缀原则(Leftmost Prefix Principle),把区分度高的字段放前面。例如
(a, b, c)索引能覆盖a、a+b、a+b+c三种查询,但不能覆盖b或c单独查询 - 覆盖索引(Covering Index):让索引包含所有需要查询的列,避免回表(Table Access by Index Rowid)。这是优化
SELECT慢查询的杀手锏 - 索引下推(Index Condition Pushdown, ICP):MySQL 5.6+,存储引擎在索引层面过滤数据,减少回表次数。EXPLAIN的Extra字段会出现
Using index condition
3.2 SQL改写
- 避免
SELECT *→ 只取需要的列,配合覆盖索引 - 避免
%keyword%前缀模糊查询 → 索引失效,改用keyword%或全文索引(Fulltext Index) - 避免在索引列上做函数运算 →
WHERE DATE(create_time) = '2024-01-01'会导致索引失效,改写为WHERE create_time >= '2024-01-01' AND create_time < '2024-01-02' - 小表驱动大表 → JOIN时用小表做驱动表(Driving Table),减少循环次数
3.3 锁与事务优化
慢查询不一定是查询慢,可能是等锁。排查方向:
SHOW ENGINE INNODB STATUS查看锁等待信息information_schema.innodb_trx表查看当前未提交的事务- 常见问题:长事务持有行锁(Row Lock)不放,导致其他查询阻塞;间隙锁(Gap Lock)在RR隔离级别下扩大锁范围
3.4 架构级优化(最后的武器)
当单条SQL已经优化到极限但仍慢,考虑:
- 读写分离:主库写、从库读,分散查询压力
- 缓存层:Redis缓存热点数据,减少数据库查询次数
- 分库分表:单表数据量超500万行(InnoDB B+Tree三层)后,按业务维度拆分
对比表格:不同类型慢查询的优化策略
| 慢查询类型 | 典型特征 | 优化手段 | 适用场景 | 优缺点 |
|---|---|---|---|---|
| 全表扫描型 | EXPLAIN type=ALL | 加索引 / 改写SQL | 小表、低频查询 | 优:改动小;缺:大表加索引耗时 |
| 排序型 | Extra=Using filesort | 优化ORDER BY索引 / 减少排序字段 | 分页查询、排行榜 | 优:效果立竿见影;缺:复合索引设计复杂 |
| 锁等待型 | 执行时间短但总耗时长 | 优化事务 / 减少锁范围 | 高并发写入场景 | 优:解决根本问题;缺:需业务配合 |
| 资源瓶颈型 | 所有SQL都慢 | 扩容 / 缓存 / 读写分离 | 流量突增 | 优:全局解决;缺:成本高 |
常见追问及要点
| 追问 | 回答要点 |
|---|---|
| 索引失效的场景有哪些? | ① 隐式类型转换(WHERE phone = 138...,phone是varchar);② 索引列做运算或函数;③ 复合索引不满足最左前缀;④ LIKE前缀模糊;⑤ OR连接非索引列;⑥ NOT IN / IS NOT NULL 可能导致放弃索引 |
| 怎么判断一条SQL是否走了覆盖索引? | EXPLAIN的Extra字段出现 Using index(注意不是 Using index condition),说明直接从索引返回数据,无需回表 |
| 线上加索引要注意什么? | ① 使用 ALGORITHM=INPLACE, LOCK=NONE(MySQL 5.6+的Online DDL)避免锁表;② 低峰期操作;③ 先分析 SHOW INDEX FROM table 看现有索引,避免重复索引(如 (a) 和 (a,b) 同时存在,前者冗余);④ 用 pt-online-schema-change 工具在超大表上安全执行 |
| 慢查询日志里SQL执行计划正常但就是慢,怎么排查? | ① 看系统指标:CPU、IOPS、内存、网络;② 检查是否有大事务或长事务阻塞;③ 看InnoDB的 buffer pool hit rate,命中率低于95%说明内存不足;④ 检查是否有定时任务或批量操作抢占资源 |
| 分页查询慢怎么优化? | ① LIMIT offset, size 的offset过大时,MySQL会扫描并丢弃前面的行,改为延迟关联(先查主键再JOIN回原表);② 用游标分页(WHERE id > ? LIMIT ?);③ 禁止深分页,业务上限制页数 |
面试回答模板(30秒版)
延伸准备(加分项)
- MySQL 8.0的优化器新特性:
EXPLAIN ANALYZE可以直接输出实际执行时间和行数(不是预估),比传统EXPLAIN更精准;SKIP SCAN和INVISIBLE INDEX的用法。 - B+Tree结构与索引设计的关系:为什么InnoDB默认页大小是16KB、三层B+Tree能存多少行数据(约2000万行),这直接决定分表阈值设计。
- 自适应哈希索引(Adaptive Hash Index, AHI):InnoDB为热点索引页自动构建哈希索引,理解它如何加速等值查询,以及为什么LRU列表管理会影响慢查询(缓冲池淘汰策略)。
想系统备战大厂大模型/Agent 开发?NiceOffer 提供 SDE+LLM 双轨 1v1 陪跑,合同保底 40w 年薪,文末扫码咨询。