explain是查看mysql执行计划的首选工具,它预测优化器行为而不实际执行;需结合performance_schema获取真实执行指标,再用慢日志回溯历史问题,三者对齐才能准确定位性能瓶颈。

用 EXPLAIN 看执行计划最直接
MySQL 里查 SQL 执行计划,EXPLAIN 是首选,它不真正执行语句,只分析优化器打算怎么走。对慢查询或怀疑索引没生效的语句,加 EXPLAIN 前缀就能看到关键信息:
-
type字段反映连接类型,ALL表示全表扫描,ref或range通常更健康 -
key显示实际使用的索引名,为空说明没走索引(注意:possible_keys只是候选,不等于真用了) -
rows是优化器预估扫描行数,和实际差距大时,往往说明统计信息过期,可运行ANALYZE TABLE更新 - 带
ORDER BY或GROUP BY的语句,若Extra出现Using filesort或Using temporary,大概率需要覆盖索引优化
想看真实执行过程得开 performance_schema
EXPLAIN 是预测,要监控真实执行中的资源消耗、等待事件、IO 次数,必须依赖 performance_schema。默认开启但部分消费者可能被关掉,先确认:
SELECT * FROM performance_schema.setup_consumers WHERE NAME LIKE 'events_statements%';
确保 events_statements_current、events_statements_history_long 对应的消费者为 ENABLED。之后查最近执行的语句:
SELECT DIGEST_TEXT, TIMER_WAIT, LOCK_TIME, ROWS_SENT, ROWS_EXAMINED FROM performance_schema.events_statements_history_long WHERE DIGEST_TEXT LIKE '%UPDATE orders%' ORDER BY TIMER_WAIT DESC LIMIT 5;
-
TIMER_WAIT单位是皮秒(10⁻¹² 秒),除以 10⁹ 得秒级耗时 -
ROWS_EXAMINED和EXPLAIN的rows不同,这是真实读取的行数,对比二者能判断优化器估算是否靠谱 - 如果
LOCK_TIME显著高于TIMER_WAIT,说明锁争用严重,不是 SQL 本身慢,而是被堵住了
SHOW PROCESSLIST 只能看“卡在哪”,不能看“为什么卡”
SHOW PROCESSLIST 或 SELECT * FROM information_schema.PROCESSLIST 能立刻看到当前连接状态,但它只告诉你线程卡在 Waiting for table metadata lock 或 Sending data 这类模糊阶段,无法定位到具体瓶颈。
- 状态为
Updating且Time持续增长?不一定在写数据,可能是二级索引回表慢,或是 MVCC 版本链遍历长 - 状态为
Locked?优先查performance_schema.data_locks和data_lock_waits,确认谁持有了什么锁 - 别依赖
Id排序判断“谁最老”,Time字段才是该连接持续处于当前状态的秒数
慢日志(slow_query_log)是回溯问题的关键证据
线上出问题后复盘,slow_query_log 是唯一能还原“当时那条 SQL 到底干了什么”的来源。务必打开并合理配置:
- 设
long_query_time = 1(秒),别用默认的 10 秒——很多业务扛不住 10 秒延迟 - 开
log_queries_not_using_indexes = ON,但注意:这会记录所有未走索引的查询,包括SELECT COUNT(*) FROM tiny_table这种无害语句,需配合过滤使用 - 日志里每条记录末尾的
# Query_time: ... Rows_examined: ...比 SQL 文本本身更有诊断价值 - 用
mysqldumpslow或pt-query-digest解析,别手动翻文本;尤其注意Rows_examined/Rows_sent比值,远大于 1 就意味着大量无效扫描
真正难的不是查执行计划,而是把 EXPLAIN 的预测、performance_schema 的实时指标、慢日志里的历史现场三者对齐——比如某条语句在慢日志里 Rows_examined=50000,但 EXPLAIN 显示 rows=100,那第一反应不是改 SQL,而是检查统计信息或是否存在隐式类型转换。










