凌晨三点的 CPU
慢日志只记录「超过阈值的已完成查询」,但老王遇到的是另一类问题:凌晨三点 CPU 突然 90%,可慢日志里什么都没有——查询可能没到阈值,也可能问题根本不是慢 SQL,而是锁等待、长事务在拖累全场。这时候需要的是 MySQL 自带的体检仪:performance_schema(性能数据采集)+ sys schema(人话视图)。8.0 默认全开,开箱即用。
第一问:谁在消耗我的时间
performance_schema 把每条 SQL 按指纹(digest)聚合——同构的查询(只差字面值)归并统计,从此 TOP SQL 不再靠猜:
SELECT DIGEST_TEXT,
COUNT_STAR AS exec_cnt,
ROUND(SUM_TIMER_WAIT / 1e12, 1) AS total_sec,
ROUND(AVG_TIMER_WAIT / 1e9, 1) AS avg_ms,
SUM_ROWS_EXAMINED AS examined
FROM performance_schema.events_statements_summary_by_digest
WHERE SCHEMA_NAME = "orders_db"
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;计时单位是皮秒,除以 1e12 换秒、1e9 换毫秒。解读两个信号:avg_ms 高的见一个修一个(EXPLAIN 走起);examined 巨大而返回极少的,是索引问题的惯犯——这和慢日志的 Rows_examined 是同一个思想,只是聚合视角。
第二问:此刻谁连着我
SELECT thd_id, conn_id, user, db, command,
statement_latency, current_statement
FROM sys.session
WHERE command != "Sleep"
ORDER BY statement_latency DESC;sys.session 是 processlist 的增强版:多出每连接当前正在执行的语句和耗时。排查「CPU 飙高」先看这里,抓到现行连接再决定 KILL。再配合第一问的 digest,能分辨「某条 SQL 偶发慢」还是「某条 SQL 被疯狂执行」。
第三问:长事务与锁等待
# 长事务:跑了多久、锁了多少行、改了多少行
SELECT trx_id, trx_started,
TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS run_sec,
trx_rows_locked, trx_rows_modified
FROM information_schema.innodb_trx
ORDER BY trx_started
LIMIT 10;
# 锁等待现场:谁堵谁、各持什么锁、SQL 原文
SELECT * FROM sys.innodb_lock_waits;长事务是第 7 篇警告过的 undo 连坐源头,run_sec 超过分钟级的都要盘问。sys.innodb_lock_waits 则把「阻塞者线程、被阻塞者线程、等待的锁、双方的 SQL」一张表摆齐,死锁隐患(第 9 篇)现场勘查神器。
彩蛋:MDL 元数据锁排查
还有一种玄学:ALTER 卡住,全表读写跟着卡死——元数据锁(MDL)。默认没开采集,先开启再看:
UPDATE performance_schema.setup_instruments SET ENABLED = "YES"
WHERE NAME = "wait/lock/metadata/sql/mdl";
SELECT * FROM performance_schema.metadata_locks
WHERE LOCK_STATUS = "PENDING";PENDING 的就是排队申请者,顺着就能找到压着表不放的长事务——大表 DDL 的完整作战在第 19 篇。
速查表
| 症状 | 第一落点 |
|---|---|
| CPU 高 / 整体变慢 | sys.session 抓现行 + digest 找 TOP SQL |
| 查询偶发变慢 | sys.innodb_lock_waits 看是否在排队 |
| 磁盘空间异常膨胀 | innodb_trx 长事务 + undo 堆积 |
| DDL 卡死连锁堵 | metadata_locks 找 PENDING 与持有者 |
| 哪些表读写最重 | sys.schema_table_statistics |
仪表盘配齐,内因看清楚了,下一篇看外因:应用的连接池怎么配,为什么 1000 个连接反而拖垮 8 核机器。
咖啡凉了,记得趁热喝。
评论 (0)