当前位置:首页 >  IDC >  服务器 >  正文

MySQL 慢查询拖垮 CPU:processlist 到 pt-query-digest

 2026-09-04 14:29  来源: 互联网   我来投稿 撤稿纠错

  一键部署OpenClaw

服务器 CPU 突然打满,网站慢得像拨号上网。登上机器一看,MySQL 占了大头。这时候最忌讳的是直接重启数据库——重启完负载确实掉了,但过不了多久它还会爬回来,因为那条慢 SQL 还在那儿等着被执行。

第一步是抓现行。SHOW PROCESSLIST 能看到此刻正在跑的语句,但默认只显示前 100 个字符,长 SQL 会被截断。用 SHOW FULL PROCESSLIST 看完整的,重点看 Time 列——执行时间长的那些就是嫌疑犯。如果同一条 SQL 反复出现,基本可以定罪。

抓现行有个前提:你得赶上。慢查询如果不是持续存在,靠手敲命令很难碰上。这时候就该开慢日志了,把 long_query_time 设低一点,让所有超过阈值的语句都留下记录,事后随时可以翻。

日志攒够之后,用 pt-query-digest 分析。它是 Percona Toolkit 里的工具,比 MySQL 自带的 mysqldumpslow 强在一点:它会把字面量抽掉做指纹归并,同一条 SQL 的不同参数会被算作一类,然后按总耗时排序。这样你看到的不是一堆零散语句,而是按影响排好队的 Top 榜。

#!/bin/bash

# 慢查询定位流水线:抓现行 -> 开慢日志 -> 出报表

# 1. 先抓现行:执行超过 5 秒的语句

mysql -e "SELECT id, user, host, db, time, LEFT(info, 120) AS sql_head

FROM information_schema.processlist

WHERE command != 'Sleep' AND time > 5

ORDER BY time DESC LIMIT 20\G"

# 2. 临时开启慢日志(重启失效,适合应急排查)

mysql -e "SET GLOBAL slow_query_log = ON;

SET GLOBAL long_query_time = 1;

SET GLOBAL slow_query_log_file = '/var/log/mysql/slow.log';"

# 3. 让它跑一段时间(比如一个业务高峰),再出报表

sleep 600

# 4. 用 pt-query-digest 按总耗时排序,取前 10

pt-query-digest --limit 10 /var/log/mysql/slow.log > /tmp/slow_report.txt

# 5. 换一个维度:按扫描行数排序,专抓缺索引的全表扫描

pt-query-digest --order-by Rows_examined:sum --limit 10 \

/var/log/mysql/slow.log > /tmp/slow_by_rows.txt

echo "报表已生成:/tmp/slow_report.txt  /tmp/slow_by_rows.txt"

报表里最该盯的是 Rows examine 和 Rows sent 这两个数的比值。扫描了一百五十万行,只返回二十行,比值接近十万比一——这就是典型的缺索引,数据库为了找你要的那点数据把整张表翻了一遍。给它加上合适的索引,CPU 立刻就下来了。

另一种情况正好相反:扫描行数不多,返回行数也不多,但耗时就是长。这种通常不是索引的问题,是写法的问题——嵌套子查询、没有 LIMIT 的分页、在循环里逐条查。这类得改 SQL 甚至改调用逻辑,加索引没用。

有个参数在生产环境上要慎用:log_queries_not_using_indexes。开了它之后所有没走索引的语句都记进慢日志,听起来很美,实际上在繁忙的库上一天能写出几十个 G,把磁盘撑爆。真要用,排查完记得关掉。

最后一句:拿到报表别急着建索引。先对那条 SQL 跑一遍 EXPLAIN,看清它现在的执行计划,确认加了索引之后 type 真的会从 ALL 变成 ref 或 range。盲建索引不但可能没效果,还会拖慢写入——每个索引都是写入时的一份额外成本。

数据来源:Percona Toolkit 官方文档

申请创业报道,分享创业好点子。点击此处,共同探讨创业新机遇!

相关标签
网站服务器
服务器

相关文章

  • MySQL 1040 Too many connections:连接泄漏定位与上限设定

    网站隔三差五报一句数据库连接失败,刷新两下又好了。这种时好时坏的毛病最烦人,因为它不像宕机那样干脆,你没法守在屏幕前等它出现。等到真去查的时候,现场早没了。MySQL从5.7起max_connections默认就是151,8.0也沿用了这个值。超过之后新连接直接被拒,报ERROR1040。看到这个错

  • PHP-FPM 进程耗尽:pm.max_children 用内存反推怎么算

    间歇性502里最磨人的一种:低峰期一切正常,流量一上来就崩,过会儿又自己好了。FPM日志里翻出serverreachedpm.max_children,基本就能确诊——子进程开到上限,新请求没进程可用了。很多教程看到这条日志就让你把pm.max_children调大,却不说调到多少。拍脑袋填个100

  • PHP 500 错误定位四步:日志、开关、堆栈、复现

    500和502的差别,一句话说清:502是Nginx拿不到上游响应,500是PHP自己跑着跑着崩了,然后把这个错误码一路传回浏览器。所以500的现场一定在PHP侧,去Nginx的error.log里翻是找不到的。第一步,确认错误到底出在哪一层。curl拿状态码只是看到表象,真正的判断依据是响应头里有

  • Nginx 504 Gateway Timeout:超时链路上每一环怎么查

    504和502经常被混为一谈,但两者的含义差得远。502是Nginx压根联系不上上游,504是联系上了、请求也发过去了,可上游迟迟不回话,Nginx等不下去了。换句话说,502是后厨关门了,504是后厨开门营业,只是做菜太慢。这个区别决定了排查方向完全不同。502查的是上游在不在,504查的是上游为

    标签:
    网站服务器
  • Nginx 502 Bad Gateway:从 PHP-FPM 到上游的四段排查

    502的全称是BadGateway,翻译过来叫坏网关。这个翻译其实挺误导人的,因为它让人以为网关坏了。真实情况是:Nginx好端端地在那儿,问题永远出在它背后的上游服务,绝大多数时候就是PHP-FPM。搞清这一点,排查方向就不会跑偏。第一段,确认FPM还活着。systemctlstatusphp-f

热门排行

信息推荐