性能突降时应先查连接数和活跃会话,再查慢SQL;80%的“慢”源于排队或等待,而非SQL本身慢。

先看连接数和活跃会话,别急着查慢SQL
性能突降时,80% 的“慢”不是 SQL 本身慢,而是被卡在排队或等待上。直接连上 MySQL 执行 SHOW PROCESSLIST,一眼就能看出有没有线程卡在 Waiting for table metadata lock、Sending data 或 Locked 状态。更准一点,用这个语句过滤掉干扰项:
SELECT ID,USER,HOST,DB,COMMAND,TIME,STATE,INFO FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep' AND TIME > 60 ORDER BY TIME DESC LIMIT 10;
重点盯 TIME 超过 60 秒且 STATE 非空闲的线程。如果发现一堆线程都卡在 Waiting for table metadata lock,基本就是某条 ALTER TABLE 或长事务没提交,得立刻定位并 KILL 源头连接。
确认慢查询日志是否开着,别等出事才补救
线上库默认常关 slow_query_log,等性能掉了再开,日志里全是“过去式”。先查状态:SHOW VARIABLES LIKE 'slow_query_log'。如果是 OFF,马上临时开启:SET GLOBAL slow_query_log = ON,同时调低阈值:SET GLOBAL long_query_time = 1.0(别设 0,会记录所有查询,IO 压力大)。
日志路径看 slow_query_log_file 变量,常见是 /var/lib/mysql/slow.log。别只扫一眼平均耗时——用 mysqldumpslow -s t -t 10 /var/lib/mysql/slow.log 找总耗时最高的前 10 条,它们才是真正的“压舱石”。高频但单次不慢的 SQL,积少成多一样拖垮数据库。
检查 InnoDB 锁和缓冲池,很多“慢”其实是等出来的
执行 SHOW ENGINE INNODB STATUS\G,重点看三块:
-
SEMAPHORES下os_waits持续上涨 → 内核级锁争用严重,可能配置或并发模型有问题 -
TRANSACTIONS里lock struct(s)数量突增,且出现waiting for this lock to be granted→ 行锁/间隙锁已形成阻塞链 -
BUFFER POOL AND MEMORY中Free buffers长期接近 0,Database pages剧烈抖动 → 缓冲池太小,或被大范围扫描刷爆
真有锁等待,用这句查阻塞源头:
SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread,
b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread,
r.trx_query waiting_query
FROM information_schema.INNODB_TRX b
JOIN information_schema.INNODB_LOCK_WAITS w ON b.trx_id = w.blocking_trx_id
JOIN information_schema.INNODB_TRX r ON r.trx_id = w.requesting_trx_id;
别漏掉统计信息过期这个“隐形杀手”
大批量导入、删除或更新后,user_logs 这类表的索引基数、列分布信息就失效了。优化器还按老数据做决策,可能放弃本该走的索引,硬切全表扫描——看起来 SQL 没变,执行计划却崩了。
简单粗暴但有效:运行 ANALYZE TABLE orders 强制刷新统计信息。这不是万能药,但对刚做完 INSERT ... SELECT 或 DELETE FROM ... WHERE 的表,往往一试就灵。注意它会加表级读锁(InnoDB 下很轻),别在高峰期对大表狂刷。
真正难搞的是那种连接数缓慢爬升、Sleep 线程越积越多的情况——那大概率是应用层没关连接,或者事务开了没提交。这种问题不会立刻报错,但会像慢性病一样,把数据库拖进响应延迟、超时、连接拒绝的死循环里。











