
1. 这个命令到底解决什么问题先直接说结论SHOW FULL PROCESSLIST是 MySQL 里排查“数据库卡住、连接堆积、SQL 跑不动”这类问题时的第一排查命令没有之一。很多人在业务正常时根本想不起它但一旦线上告警“连接数打满”或者“某个接口超时”能救场的往往就是这条命令。它的作用简单粗暴把当前所有客户端连接正在执行的 SQL、连接状态、耗时、来源 IP、所操作的库表全部列出来。相当于给数据库做了一个“现场快照”让你一眼看清此刻到底是谁在占用资源、哪条 SQL 在拖垮实例。我当年第一次接触这条命令是在一个非常狼狈的场景里某内部系统的管理后台突然卡死页面转圈十几秒才出来数据库 CPU 飙到 90% 以上。当时还没什么排查经验第一反应是重启实例好在被旁边的同事拦住了。他执行了一下SHOW FULL PROCESSLIST很快就发现一条没有加索引的UPDATE语句锁住了大量行导致后续所有写操作都在排队。从那以后我就把这条命令当成了 MySQL 排障的“第一板斧”每次遇到数据库性能问题都先用它看一眼现场。谁需要掌握这个命令不只是 DBA只要你的业务跟 MySQL 打交道——后端开发、运维、测试、数据分析都应该至少会用。因为你不需要权限去改服务器配置也不需要看懂执行计划只需要能跑一条命令、能看得懂输出结果就能在关键时刻定位到问题的方向避免“一遇到问题就重启”这种最粗暴的处理方式。2. 核心输出字段逐个解读执行SHOW FULL PROCESSLIST注意有FULL关键字后面会细说区别之后MySQL 会返回一张表常见的字段如下字段名含义Id连接的唯一标识杀掉连接时用的就是这个 IDUser连接对应的 MySQL 账户Host客户端来源地址和端口db当前执行语句所在的数据库可能为空Command当前连接正在执行的操作类型如 Query、Sleep、LockedTime当前状态持续的时间单位秒State执行过程中的具体状态这条最值得关注Info正在执行的 SQL 文本FULL关键字决定了它是否完整显示2.1 Info 字段为什么必须加 FULL不加FULL的时候Info列只会显示 SQL 的前 100 个字符。如果是一条很长的INSERT或者带大量子查询的SELECT你看到的就是一句被截断得七零八碎的 SQL根本无法判断它到底在查哪些表。加了FULL之后MySQL 会显示完整的 SQL 文本。这一点在排查复杂语句时尤其重要。举个例子你看到某条连接 Time 达到了几百秒如果不看完整 SQL你只能猜到“有语句卡住了”但完全不知道卡在哪张表上有了完整的 SQL 文本你就能快速判断它涉及哪些表、关联条件是什么、大致能猜出问题出在哪。注意即便加了 FULL如果 SQL 本身是超过max_allowed_packet大小的批量语句显示出来的内容仍然可能不完整或者是截断后的片段。这种情况通常出现在导入大文件或者批量写入时。2.2 State 字段判断卡点真正的位置State字段直接告诉你当前执行的 SQL 正在 MySQL 内部的哪个阶段。常见的 State 类型有Sending data正在读取数据并发送结果。这个状态比较复杂它既包含真正的读取数据也包含排序、临时表操作等过程。很多慢查询出现时State 就停在这里。Waiting for table metadata lock等待元数据锁。通常是某个表上有未提交的ALTER TABLE、DROP TABLE或TRUNCATE操作占住了锁其他对这个表的读写都在等待。Waiting for table flush等待FLUSH TABLES操作完成常见于备份脚本触发了表刷新。Waiting for handler commit等待存储引擎提交通常与InnoDB的提交过程有关也可能是磁盘 I/O 太慢。Updating/Updating main table正在更新行数据如果长时间停留且伴随大量锁等待就要考虑行锁冲突。实际排查时State 比 Command 更有信息量。因为Query只能说明“这个连接正在跑 SQL”而 State 能告诉你“它到底卡在哪一步”。比如同样是QueryState 为Waiting for table metadata lock和Sending data后续的处理方向完全不一样前者要去找锁持有者后者要分析 SQL 本身有没有问题。2.3 Command 列常见的几种状态Sleep连接空闲没有执行任何语句。大量Sleep连接通常是连接池没有正确释放、或者应用侧使用了长连接却没有复用机制。Query正在执行查询或写入语句是排查的重点对象。Binlog Dump该连接正在向从库同步二进制日志属于复制线程的正常状态。Killed连接被下发KILL命令等待终止。如果一直处于这个状态说明线程无法被快速中断。Connect连接正在建立如果这个状态大量堆积通常意味着连接数满了。2.4 Time 字段别只盯最大值Time字段代表当前状态的持续秒数。很多人喜欢看“最大 Time”认为耗时最长的就是问题根源。但实际上把 Time 从小到大排序反而更有用Time 很大的连接可能是历史遗留的老查询而 Time 不大但数量极多的连接才更值得警惕。举个例子有一条跑了 500 秒的查询它占用的可能只是自己的线程资源不影响别人但如果有几十条 Time 只有 10 秒的写查询同时在排队等待锁那才是真正让业务变慢的元凶。所以排查时不能只看单个大数值要把整体分布情况都扫一遍。3. 高频使用场景与实战案例3.1 场景一发现大量 Sleep 连接堆积某个业务上线后监控面板显示 MySQL 连接数从原来的 200 稳定涨到 800最高时逼近 max_connections。第一次遇到这种问题时很容易误判为“SQL 慢导致连接堆积”但实际上跑一下SHOW FULL PROCESSLIST就会发现几百条连接的 Command 都是SleepState 为空Time 从几十秒到几小时都有。这种情况的根本原因基本都在应用侧要么使用了长连接却没有超时回收机制要么连接池的maximumPoolSize配置得太大要么业务代码里每次请求都新建连接但不释放。MySQL 本身并没有做错什么它只是忠实保持着这些空闲连接。处理方案分两步。第一步是临时止血直接 KILL 掉那些 Time 特别长的 Sleep 连接释放连接数。第二步才是根治检查应用连接池配置增加空闲连接超时回收、限制最大连接数。这里建议先做第二步不要一上来就无脑 KILL因为某些框架的连接池会定期复用长连接你杀掉之后应用会自动重连但反复杀掉和重连反倒增加额外开销。3.2 场景二SQL 卡死并且伴随行锁等待遇到过很多次这样的情况数据库 CPU 并不高但业务就是卡。执行SHOW FULL PROCESSLIST后看到大量连接处于Waiting for table metadata lock或者Waiting for handler commit状态指向的都是同一张表。这种问题的典型链路是某个事务没有及时提交持有了一部分行锁或表锁后面所有需要操作这些数据的语句都堵在锁等待上。而持有锁的事务本身State 往往是Sleep这就更隐蔽了——一个空闲连接占着锁不放业务表现为数据库“假死”。排查思路是找到 First 那个 Time 最短的写事务用SELECT * FROM information_schema.innodb_trx查看事务详情找到对应的trx_mysql_thread_id然后结合SHOW FULL PROCESSLIST里的 Id 对应起来。如果是业务上的“查询未提交事务”可以 KILL 掉如果是应用代码里忘记提交就需要去改代码了。3.3 场景三慢查询集中爆发当监控发现一批接口的超时时间明显上升时SHOW FULL PROCESSLIST的结果往往是多条SELECT语句的 State 是Sending data并且 Time 集中在几十秒以上。这里要区分“语句本身慢”和“语句在等待资源”两种情况如果同一条 SQL 在慢查询日志里反复出现那大概率是语句本身的问题比如缺失索引、索引失效、全表扫描、大范围排序等。如果 SQL 本身看起来正常但每次都卡在同一个状态上则要考虑是不是有其他会话在抢占资源或是磁盘 I/O 能力跟不上。实操层面我通常会在执行SHOW FULL PROCESSLIST的同时另外开一个会话跑SHOW ENGINE INNODB STATUS查看锁等待和事务信息。两个命令搭配起来纵向能看到 SQL 的等待时长横向能看出锁的持有链路。3.4 场景四连接数被打满Too many connections是很多人遇到过的报错。这时你可能连SHOW FULL PROCESSLIST都执行不了因为新连接无法建立。有两个办法一是用 MySQL 的保留连接通道在命令行里以管理员权限连入mysql -uroot -p --protocolsocket二是如果 root 也连不上只能通过重启数据库临时恢复连接数但这是最后的办法不建议优先使用。能连上之后立刻执行SHOW FULL PROCESSLIST找出占用连接的元凶。如果确实找不到具体来源至少可以根据Host字段看到哪些 IP 占用了大量连接然后从应用侧去查问题。4. 进阶用法与配套命令SHOW FULL PROCESSLIST单独用就已经很强但在实际运维中往往需要跟其他命令配合才能更精准地定位问题。4.1 配合 information_schema 查询活跃事务通过信息模式表可以直接查询当前正在运行的事务、锁等待情况比纯靠 processlist 的可见信息更深入SELECT * FROM information_schema.innodb_trx; SELECT * FROM information_schema.innodb_locks; SELECT * FROM information_schema.innodb_lock_waits;前一张表能看到事务的运行时间、锁等待时间、对应的线程 ID。后两张表在新版本 MySQL 中可能返回空因为锁信息迁移到了performance_schema此时可以用SELECT * FROM performance_schema.data_locks; SELECT * FROM performance_schema.data_lock_waits;实际排查一条锁等待问题我会这样串联起来执行SHOW FULL PROCESSLIST找到 State 为Waiting for ...的连接记录 Id。用上面的事务表查出持锁事务的trx_mysql_thread_id。拿这个线程 ID 回过去对比 processlist就能定位到是哪个应用、哪条 SQL 占着锁不放。4.2 配合 performance_schema 做更细粒度的观测如果要把 processlist 的结果落到监控图表上performance_schema的表查询就比 text 输出更适合做自动化采集。常用的一条 SQL 是按 THREAD_ID 查询当前线程正在执行的语句SELECT THREAD_ID, PROCESSLIST_ID, CURRENT_SCHEMA, LEFT(SQL_TEXT, 100) FROM performance_schema.events_statements_current WHERE SQL_TEXT IS NOT NULL;相较于SHOW FULL PROCESSLISTevents_statements_current能看到更细的语句阶段、扫描行数、临时表使用情况等。典型的做法是让监控脚本定时把两种数据都采集下来processlist 用于快速判断performance_schema 用于深挖执行细节。4.3 自动采集的脚本思路单纯靠人工敲命令效率很低尤其是在问题持续几分钟又不好复现的时候。我之前会写一个小脚本每隔 2 秒采样一次 processlist 并记录到文件里while true; do mysql -uroot -p密码 -e SHOW FULL PROCESSLIST\G /tmp/processlist_$(date %Y%m%d_%H%M%S).log sleep 2 done采样频率不用太高1 到 2 秒一次足够了。问题如果是间歇性的可以提前挂上这个脚本等问题发生后再去翻阅日志。这不仅对排查有用还能沉淀成文档案例下次遇到类似问题直接翻历史记录。4.4 误区提醒不要对比不同时区的 Time如果 processlist 是手动采集的Time 字段比较直观。但如果采集脚本带上了时间戳要注意 MySQL 的Time是“从语句开始到现在”的秒数不是墙上时钟时间。同一秒内两次采样Time 差应该非常小如果发现某条连接 Time 从 10 跳到 100那说明这条 SQL 真的卡了很久。不要因为时间戳变了就误判。5. 常见问题与排查技巧实录5.1 INFO 列显示不完整前面提到过不加FULL只显示前 100 个字符加上FULL后会显示完整 SQL。但有一种情况需要说明如果 SQL 中包含引号、换行、特殊字符终端显示时可能出现转义或者折行复制的 SQL 不能直接拿去执行。这时候可以把结果导入临时表或者用\G竖排显示保持可读性。5.2 线程一直处于 Killed 状态如果你对某条连接执行了KILL id但连接始终停在 Command Killed说明这个线程没有及时响应终止信号。常见原因是正在执行一个无法被中断的内部操作比如大量磁盘 I/O 排序、超大临时表创建等。此时只能等待操作自然结束或者考虑重启实例但重启会中断所有连接影响面很大需要权衡。5.3 权限不足看不到其他用户的 SQL普通账号默认情况下SHOW FULL PROCESSLIST只能看到自己当前账号的连接。如果你需要看到所有用户的连接必须有PROCESS权限GRANT PROCESS ON *.* TO 你的账号%;这个权限一般由管理员分配但对开发排查问题来说非常实用。没有权限时你只能通过performance_schema.events_statements_current间接获取部分语句信息权限要求相对宽松一些。5.4 空结果别高兴太早如果你执行SHOW FULL PROCESSLIST后什么结果都没有那确实是“现在没有活动连接”但这不代表没有性能问题。有些性能瓶颈是历史遗留的比如之前跑的慢查询已经结束但缓存池被污染、磁盘被占满等问题仍然存在。所以 processlist 只是“当下快照”不是性能问题的全部答案需要结合监控历史数据一起看。5.5 别把 KILL 当万能药最容易被低估的坑就是看到哪条 Time 大就 KILL 哪条杀完发现业务还是卡。原因很简单KILL 只能终止“正在执行的语句”无法修复导致语句变慢的根本原因——缺索引还是缺索引锁表还是锁表。举个例子如果一条查询本身没有大问题但外部有大批量数据写入占着磁盘KILL 了这条查询新请求进来还会继续卡。所以 KILL 只是应急手段正确的做法是先找出为什么卡再决定要不要杀。5.6 排查时区分连接类型实际排查时SHOW FULL PROCESSLIST里会混着很多线程不全是业务查询。像复制线程Binlog Dump、系统内部线程也会出现在输出里。建议先对Command做一次分类把业务查询和非业务线程分开避免被无关线程干扰判断。我之前经历过一次误判把复制线程当成了业务 SQL折腾了半天才发现是从库拉取日志的线程业务侧完全正常。所以这条命令虽然简单但看结果时一定要带脑子先分清类型再动手。5.7 长时间 Sleep 与连接泄漏的判别如果大量 Sleep 连接一直存在可从两个维度判断到底是正常空闲还是连接泄漏看连接数量是否随时间线性增长。如果是不断增长属于泄漏如果是稳定在某个区间可能是业务自身的空闲连接策略。看应用侧的线程池配置。如果连接池上限是 500而 processlist 一直保持 499 个 Sleep那不是泄漏是池子撑满了需要检查池内等待任务的堆积情况。还有一种情况是连接池在低峰期把连接收缩到很小高峰期再重建。如果业务响应变差不要急着杀 Sleep可以先看看池子的获取连接超时时间是不是设置得太短导致连接池还没来得及归还连接就被业务端认为超时了。6. 如何快速定位并处理异常连接6.1 常用定位 SQL下面这条 SQL 是我每次排查时必跑的“标准动作”能把 processlist 的结果按特征进行聚合快速看出问题集中在哪里SELECT User, Host, db, Command, State, COUNT(*) AS cnt, MAX(Time) AS max_time FROM information_schema.processlist GROUP BY User, Host, db, Command, State ORDER BY cnt DESC, max_time DESC;相比直接看原始输出这种聚合方式能快速暴露问题的集中度。比如看到一堆连接集中在同一个 Host、同一个 Command那就说明问题很可能来自某一个应用节点如果 State 集中在Waiting for table metadata lock那就说明锁问题。6.2 单条 SQL 的重型现场记录如果想留档比对建议使用\G竖排输出方便记录整条 SQL 的完整信息SHOW FULL PROCESSLIST\G这个输出适合人工阅读但不适合做统计分析。自动化采集仍然建议用默认的表格格式或者直接把输出导入到文件再用脚本解析。6.3 处理连接的基本原则先看后杀杀连接前务必看一眼 Owner 是谁。很多公司会有多个业务共用同一个 MySQL 实例如果杀掉别人的连接可能引发更大的问题。比较稳妥的做法是先确认连接对应的账号和应用节点 IP。如果连接来自某个已知的定时任务或 ETL 任务先联系相关归属人确认是否可以中断。确认之后再执行KILL id。这里再补充一点KILL有两种方式一种是KILL QUERY id只终止当前正在执行的查询保留连接另一种是KILL id直接断开整个连接。如果你只是想让某个卡住的会话恢复正常尽量用KILL QUERY避免把连接上下文也一起清掉。实际经验里误用KILL直接断开连接导致应用出现“connection lost”的案例并不少见。6.4 查询刷屏时怎么记录现场有时候慢 SQL 一波接一波人工盯屏幕根本盯不过来。这种情况下我一般建议先快速记录下面几个关键信息再决定要不要中断当前最大 Time 值出现次数最多的 State涉及的表名是否有多个连接指向同一张表这四条够了。记录完之后可以再针对具体的表执行SHOW INDEX FROM 表名看看索引是否合理或者用EXPLAIN分析一下语句的执行计划。这些操作都不会影响线上运行可以大胆做。7. 这条命令不能替代什么SHOW FULL PROCESSLIST强在“直观、即时”但它有几个天然盲区只能看当前时刻的状态看不到历史趋势。如果是间歇性慢查询只在某几秒内卡顿恰好你不在这几秒内执行命令就不会看到任何异常。只能看连接级别信息看不到单条语句的内部执行细节比如扫描了多少行、是否用了临时表、是否产生了磁盘排序。只能看 MySQL 层的信息看不到操作系统层和硬件层的问题比如内存 swap、磁盘 I/O 延迟、CPU 调度异常这些需要结合系统监控数据。所以在正式排障流程里我不会只依赖这一条命令。我会同时关注慢查询日志、监控平台的指标曲线、错误日志再综合判断。如果应用报错集中在某个时间段我会把慢查询日志和 processlist 采样数据做对齐看是不是同一批语句触发了问题。拿慢查询日志来说它记录的是执行超过long_query_time的语句里面包含了语句耗时、执行时间、扫描行数、返回行数。相比 processlist 的“快照”慢查询日志更像“账本”能回溯历史问题。两条配合使用才能既看清当前现场又还原问题的时间线。8. 一些值得长期沉淀的习惯最后分享几个我踩坑之后养成的习惯希望能帮你少走弯路第一把SHOW FULL PROCESSLIST的输出存下来。哪怕是手动执行的在问题排查结束后也建议把当时的输出保存到本地或内部文档。不要觉得“问题已经解决了就没必要留”下次遇到类似情况翻出历史记录就能快速对比判断是偶发还是趋势性问题。第二定时对 processlist 做一次扫描。如果公司的运维体系允许建议每隔 5 分钟自动跑一次并汇总到监控系统。不需要保留太多细节只需要记录四个维度连接总数、Query 数量、Sleep 数量、锁等待相关状态的数量。这四个指标一旦有异常波动就能第一时间感知。第三写故障复盘时把 processlist 截图放进文档里。它不是唯一的证据但它是现场感最强的证据。很多后续优化方向的判断比如“是不是需要给某张表加索引”“是不是某个接口存在连接泄漏”都可以从一张 processlist 截图里看出端倪。我自己用过最多的方式是在故障结束后把 processlist 输出导入分析表按照User Host State分组统计各个组合出现的次数和最大耗时。这种做法帮我在不少“看似随机、实则集中”的问题里找到了规律。还有一个很多人都忽略的点SHOW FULL PROCESSLIST的结果里如果出现了大量Waiting for table flush先别急着责怪应用有可能是你在同一时间执行了备份命令或者FLUSH TABLES WITH READ LOCK。这类操作虽然是 DBA 层面发起的但它对业务的影响比想象中大得多。以上这些经验基本上都是靠着一条命令 多次重新认识才慢慢积累下来的。如果你刚接触 MySQL 运维我建议从今天开始遇到任何数据库相关的异常先花十秒钟执行一下这条命令再看其它监控。这个习惯能帮你避开很多“瞎猜”式的排障弯路。