1.21
SELECT
o.id,
o.user_ids,
GROUP_CONCAT(u.name SEPARATOR ',') AS user_names
FROM t_orders o
LEFT JOIN t_user u ON FIND_IN_SET(u.id, o.user_ids) > 0
GROUP BY o.id, o.user_ids;
好的,这是一个非常典型的 MySQL 性能问题。当 SELECT 查询一直处于“等待”状态(在 SHOW PROCESSLIST 中显示为 Sending data, Locked, 或 Waiting for table level lock 等)时,通常意味着有东西阻塞了它。
别担心,我们可以按照一个清晰的排查流程来定位并解决问题。
核心思路
-
找到罪魁祸首:首先找出是哪个进程/查询在阻塞其他所有操作。
-
分析阻塞原因:是慢查询、锁竞争、还是系统资源耗尽?
-
采取行动:根据原因,选择终止问题查询、优化SQL、或者进行系统调优。
第一步:立即排查 - 找到阻塞源
- 检查当前所有连接和状态
这是最重要的一步,能直接看到谁在“作祟”。
-- 进入 MySQL 命令行后执行
SHOW FULL PROCESSLIST;
重点看 State 列,这里会显示每个连接正在做什么。你需要寻找:
• Locked: 表被锁住了。
• Waiting for table level lock: 在 MyISAM 等存储引擎上,一个写操作会锁住整张表,导致读也被阻塞。
• Copying to tmp table: 正在处理一个很复杂的查询,可能正在创建临时表,如果数据量大,会非常慢。
• Sorting result: 正在对结果集进行排序,如果数据量巨大,也很耗时。
• Sending data: 这通常不是一个好信号。它可能表示查询本身效率极低(比如缺少索引的全表扫描),导致MySQL需要花费大量时间在磁盘和网络上传输数据。
• statistics / creating sort index: 可能在生成执行计划或进行排序。
特别关注:
• 查找 Command 列为 Query 且 Time (执行时间) 很长(比如几百秒甚至上千秒)的连接。
• 查找 State 为 Locked 的连接,特别是如果有多个这样的连接,很可能就是一个长事务在写操作。
- 识别阻塞者和被阻塞者
MySQL 8.0 提供了一个强大的功能来可视化锁等待关系。如果你的版本较低,这个方法不可用。
SELECT * FROM performance_schema.data_lock_waits;
SELECT * FROM sys.innodb_lock_waits; -- 更友好的视图,推荐
这个查询会清晰地告诉你:哪个线程(请求方)正在等待哪个线程(阻塞方)持有的锁。找到那个持有锁并且运行时间最长的线程ID (blocking_pid)。
第二步:分析原因并采取行动
找到可疑的进程ID (Id) 后,我们来分析原因。
场景 A:发现一个长时间运行的慢查询 (最常见)
如果 SHOW PROCESSLIST 显示一个 SELECT 语句已经跑了很久。
- 获取完整SQL:
SELECT Id, User, Host, db, Command, Time, State, Info FROM INFORMATION_SCHEMA.PROCESSLIST WHERE Id = [可疑进程的ID];
(这里的 Info 字段就是完整的 SQL)
- 分析原因:
◦ 把这个 SQL 拿出来,在 MySQL 命令行里加上 EXPLAIN 前缀运行一下:EXPLAIN SELECT ...。
◦ 查看输出结果,type 是否为 ALL(全表扫描),key 是否为 NULL(未使用索引)。如果是,那就是典型的索引缺失问题。
- 采取行动:
◦ 短期:如果这个查询不重要,可以直接终止它。请务必谨慎!
KILL [可疑进程的ID];
◦ 长期:为相关的表和字段添加合适的索引。
场景 B:发现大量的 Locked 或 Waiting for table level lock
这通常是 MyISAM 引擎的标志性问题。当一个会话对一个 MyISAM 表执行 UPDATE, DELETE, INSERT 时,它会锁定整个表,在此期间任何其他的 SELECT 或其他写操作都必须排队等待。
- 确认引擎:
SHOW CREATE TABLE your_table_name;
- 采取行动:
◦ 终极解决方案:将表的存储引擎从 MyISAM 转换为 InnoDB。
ALTER TABLE your_table_name ENGINE=InnoDB;
InnoDB 支持行级锁,可以极大减少这种锁竞争,是现代MySQL的默认和标准选择。
◦ 短期缓解:耐心等待当前的写操作完成。尽量避免在生产环境的MyISAM表上进行长时间的写入操作。
场景 C:系统资源耗尽 (CPU, IO)
有时候不是单个慢查询,而是系统整体负载过高。
- 在服务器上检查系统资源:
◦ top 或 htop: 查看 CPU 使用率,看是不是 mysqld 进程占用了 100% 的 CPU。
◦ iostat -x 1: 查看磁盘 I/O 使用率 (%util)。如果持续接近 100%,说明磁盘是瓶颈,很可能是慢查询在疯狂读写。
- 采取行动:
◦ 如果确认是系统资源问题,那么根本原因还是慢查询。需要结合 SHOW PROCESSLIST 和 EXPLAIN 来优化最耗资源的那些查询。
◦ 考虑增加服务器配置,如升级CPU、内存,或者更换更快的SSD硬盘。
总结与建议的排查步骤
-
第一反应:SHOW FULL PROCESSLIST; -> 找 Time 长、State 异常的 Query。
-
精准定位:SELECT * FROM sys.innodb_lock_waits; (如果版本支持) -> 找到阻塞链。
-
判断类型:
◦ 慢查询 -> 用 EXPLAIN 分析,加索引,或 KILL 掉。
◦ 表锁/行锁 -> 检查存储引擎,考虑转成 InnoDB;或 KILL 掉持锁不释放的长事务。
-
系统层面:top, iostat -> 检查是否因高并发或资源不足导致。
-
长期预防:
◦ 开启慢查询日志:slow_query_log = ON, long_query_time = 1。让MySQL自动记录下所有执行时间超过1秒的查询,方便定期分析。
◦ 使用监控工具:如 Percona Monitoring and Management (PMM), Zabbix 等,可以提前发现性能趋势。
◦ 建立性能基线和规范:对应用开发团队进行培训,避免写出全表扫描的SQL。
请先从第一步开始,把你看到的 SHOW FULL PROCESSLIST 的输出中最关键的几条发给我(记得去掉敏感信息),我可以帮你做更精确的判断。

浙公网安备 33010602011771号