用 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,而是检查统计信息或是否存在隐式类型转换。
