面试知识库
困难

MySQL执行计划与主从延迟实战#

一句话答案#

EXPLAIN 看执行计划重点关注 type(ALL 全表扫描必须优化)、key(实际用了什么索引)、rows(扫描行数)、Extra(Using filesort/temporary 是性能杀手);主从延迟核心看 Seconds_Behind_Master,常见原因是大事务、DDL、从库性能不足。

核心要点

一、EXPLAIN 字段逐项解读

字段含义关注点
type访问类型ALL<index<range<ref<eq_ref<const,ALL 必须优化
possible_keys可能用的索引有但没用=索引失效
key实际用的索引NULL=全表扫描
key_len索引使用长度联合索引是否用满
rows预估扫描行数越小越好
filtered过滤百分比100%=无额外过滤
Extra额外信息filesort/temporary/index condition pushdown

type 从差到好排序:

ALL → index → range → index_merge → ref → eq_ref → const → system
全表   全索引   范围     索引合并      非唯一索引 唯一索引  主键常量
plaintext

Extra 关键词:

Extra 值含义建议
Using filesort无法用索引排序,额外排序建索引覆盖 ORDER BY
Using temporary需要临时表(GROUP BY/DISTINCT)优化查询或加索引
Using index覆盖索引,不回表最优
Using index condition索引条件下推(ICP)正常优化
Using whereServer 层过滤看能否下推到索引层

二、真实慢 SQL 案例

案例1:联合索引最左前缀失效

-- 索引: idx_status_created (status, created_at)
-- 慢SQL:
SELECT * FROM orders WHERE created_at > '2026-01-01';
-- EXPLAIN: type=ALL, key=NULL (跳过了status,最左前缀失效)

-- 修复: 加上status条件或单独建created_at索引
SELECT * FROM orders WHERE status IN (1,2,3) AND created_at > '2026-01-01';
-- EXPLAIN: type=range, key=idx_status_created
sql

案例2:隐式类型转换导致索引失效

-- 索引: idx_phone (phone VARCHAR(20))
-- 慢SQL:
SELECT * FROM users WHERE phone = 13800138000;  -- 数字比较
-- EXPLAIN: type=ALL (隐式转换VARCHAR→数字,索引失效)

-- 修复: 字符串类型传字符串
SELECT * FROM users WHERE phone = '13800138000';
-- EXPLAIN: type=ref, key=idx_phone
sql

案例3:回表代价过高

-- 索引: idx_user_id (user_id)
-- 慢SQL: 查询SELECT *返回大量字段
SELECT * FROM orders WHERE user_id = 100 ORDER BY created_at DESC LIMIT 100;
-- EXPLAIN: type=ref, rows=50000, Extra=Using filesort
-- 问题: 回表5万行后再排序

-- 修复: 建联合索引覆盖查询+排序
ALTER TABLE orders ADD INDEX idx_user_created(user_id, created_at);
-- EXPLAIN: type=ref, rows=100, Extra=Using index (覆盖索引+索引排序)
sql

三、主从延迟排查

监控命令:

SHOW SLAVE STATUS\G
-- 关键字段:
-- Seconds_Behind_Master: 延迟秒数(0=同步,NULL=复制断开)
-- Relay_Master_Log_File + Exec_Master_Log_Pos: 当前回放位置
-- Last_SQL_Error: 最近SQL线程错误
sql

延迟常见原因及解决:

原因特征解决
大事务主库一个事务binlog很大(如批量UPDATE百万行)拆分大事务为小批次
DDLALTER TABLE 锁表期间从库阻塞用 pt-osc/gh-ost 在线DDL
从库性能差从库机器配置低升配/SSD/多从库分担
单线程回放MySQL 5.6前只有一个SQL线程升级到5.7+并行复制
大量写入主库写入 QPS 突增增加从库/分库分表
锁等待从库有长查询阻塞回放排查从库慢查询

四、读写分离一致性保障

方案原理适用场景
强制走主库写后读直接查主库写后立即读(如下单后查订单)
等待 GTID写后记录GTID,读从库时等该GTID回放完要求一致但可接受短等待
中间件路由按业务场景自动路由主/从ProxySQL/ShardingSphere
缓存兜底写后把结果写缓存,读优先走缓存高频读场景

五、执行计划优化 checklist

□ type 是否为 ALL/index?→ 加索引或优化查询条件
□ key 是否为 NULL?→ 检查索引是否失效(类型转换/函数/OR)
□ rows 是否过大?→ 缩小范围或加更精确索引
□ Extra 有 filesort?→ 索引覆盖 ORDER BY 字段
□ Extra 有 temporary?→ 优化 GROUP BY 或加索引
□ key_len 联合索引是否用满?→ 检查最左前缀
□ filtered 太低?→ 索引选择性差,考虑换索引
plaintext
面试回答(2分钟版)

看执行计划我重点关注四个字段。type 表示访问类型,ALL 全表扫描必须优化,至少要到 range 或 ref 级别。key 看实际用了什么索引,NULL 说明索引失效需要排查原因——常见的是最左前缀不满足、隐式类型转换、对索引列用了函数。rows 是预估扫描行数,配合 filtered 判断索引选择性。Extra 里 Using filesort 和 Using temporary 是性能杀手,前者说明排序没走索引,解决方案是建联合索引覆盖 ORDER BY。主从延迟我通过 SHOW SLAVE STATUS 的 Seconds_Behind_Master 监控,常见原因是大事务、在线 DDL、从库单线程回放。大事务要拆批次,DDL 用 gh-ost,回放瓶颈升级 MySQL 5.7+ 并行复制。读写分离的一致性保障方面,写后读场景强制走主库,或者用 GTID 等待机制确保从库已回放到位。

追问与易错

追问方向:

  • “索引失效的常见场景?”→ 隐式类型转换、对索引列用函数或计算、LIKE 左模糊、OR 连接非索引列、!= / NOT IN 否定条件、IS NOT NULL 看优化器判断;本质是让 B+ 树无法按序定位
  • “EXPLAIN 的 rows 准确吗?”→ 不准确,是基于索引统计信息的估算值;可通过 ANALYZE TABLE 刷新统计信息提高准确度,但仍非精确值
  • “怎么监控主从延迟?”→ SHOW SLAVE STATUS 的 Seconds_Behind_Master 是基础指标;pt-heartbeat 通过写入时间戳更精确;生产环境配合 Prometheus + Grafana 设置告警阈值
  • “并行复制怎么配?”→ MySQL 5.7+ 设置 slave_parallel_workers=4~16、slave_parallel_type=LOGICAL_CLOCK,按组提交的事务并行回放,大幅降低从库延迟

易错点:

  • ❌ “type=index 就是好的”——index 是全索引扫描,只是不回表而已,仍然很慢
  • ❌ “Seconds_Behind_Master=0 就没延迟”——网络断开时也显示0,要结合 IO/SQL thread 状态判断
  • ❌ “加了索引就一定会用”——优化器可能认为全表扫描更快(小表/选择性低)
  • ✅ 看执行计划的口诀:type→key→rows→Extra,四步定位问题