直接查events_statements_summary_by_digest可快速定位高延迟update:按sql模板聚合,筛选digest_text like 'update %'且平均耗时超1秒,优先关注高频又慢的组合;系统卡顿时辅以show full processlist和innodb_trx查实时阻塞与长事务。

查 performance_schema.events_statements_summary_by_digest 找高延迟 UPDATE
直接看聚合后的 SQL 模板耗时,比翻慢日志快得多,尤其适合线上紧急排查。它把相同结构的 SQL(如 UPDATE users SET status=? WHERE id=?)归为一类,按平均延迟排序,能一眼揪出真正拖垮系统的 UPDATE 类型。
执行这句:
SELECT DIGEST_TEXT, COUNT_STAR, AVG_TIMER_WAIT/1000000000 AS avg_sec, SUM_TIMER_WAIT/1000000000 AS total_sec FROM performance_schema.events_statements_summary_by_digest WHERE DIGEST_TEXT LIKE 'UPDATE %' AND AVG_TIMER_WAIT > 1000000000 -- 平均超1秒 ORDER BY AVG_TIMER_WAIT DESC LIMIT 5;
-
DIGEST_TEXT是标准化后的语句,可快速识别模式(比如是否含子查询、IN列表过长) - 优先关注
COUNT_STAR高 +avg_sec高的组合——高频又慢的 UPDATE 最危险 - 若
DIGEST_TEXT显示为UPDATE ... WHERE ?,说明参数化明显,但实际执行可能因数据分布不均而波动大
用 SHOW PROCESSLIST 看正在卡住的 UPDATE 连接
当系统已卡顿,performance_schema 的聚合数据可能还没刷新,这时 SHOW FULL PROCESSLIST 是唯一实时窗口。重点不是找“运行时间长”,而是找“卡在某个状态不动”的 UPDATE。
- State =
Updating或Waiting for table metadata lock:大概率被 DDL(如ALTER TABLE)阻塞 - State =
Locked或Waiting for commit lock:可能和长事务或 binlog 写入瓶颈有关 - State =
Sending data(对 UPDATE 出现此状态很反常):说明优化器误判了执行计划,正在全表扫描后更新
记下对应 ID,再查 SELECT * FROM information_schema.INNODB_TRX WHERE trx_mysql_thread_id = ?,确认是否持有锁、事务是否异常长。
为什么 EXPLAIN 不适用于 UPDATE?得换招
EXPLAIN UPDATE 在 MySQL 5.6+ 虽然语法合法,但输出的 rows 和 type 只反映“查找阶段”,不包含“加锁范围”和“实际更新行数”。一个 UPDATE 卡住,往往不是查得慢,而是锁得重、回滚段争抢或二级索引维护开销大。
- 真正要盯的是
INFORMATION_SCHEMA.INNODB_LOCK_WAITS和INNODB_TRX,看谁在等谁、等什么锁 - 若 UPDATE 带子查询(如
UPDATE t1 SET x = (SELECT y FROM t2 WHERE ...)),子查询部分可用EXPLAIN单独分析,主 UPDATE 本身无法靠它定位 - 别信
EXPLAIN里key显示用了索引就安全——如果WHERE条件走索引但更新字段是没索引的TEXT列,仍可能触发大量页分裂
UPDATE 慢的三个隐蔽坑点
很多 UPDATE 表面看条件简单,实际慢得毫无征兆。最容易被忽略的是这三类:
- 隐式类型转换:
WHERE user_id = '123'(user_id是BIGINT),导致索引失效,全表扫再逐行转换 - 触发器链:
UPDATE触发了多个触发器,每个又去更新其他表,形成锁等待环或 CPU 爆高 - 外键级联:
ON UPDATE CASCADE在大表上会递归更新关联行,EXPLAIN完全不体现这部分开销
这类问题不会出现在慢日志里(因为单条执行可能不到阈值),却会让并发 UPDATE 集中时系统雪崩——必须结合 performance_schema.data_locks 和业务逻辑交叉验证。











