Quartz 引起的MySQL锁等待超时问题排查记录
问题描述:
应用程序(特别是 Quartz 调度器)出现 Lock wait timeout exceeded
异常,导致任务调度等操作失败。
排查过程:
步骤 1:初步分析 INNODB MONITOR OUTPUT
(第一次)
- 执行
SHOW ENGINE INNODB STATUS\G
命令,查看 InnoDB 引擎的内部状态信息,包括最近一次检测到的死锁信息。 LATEST DETECTED DEADLOCK
部分显示的是一个较早发生的死锁事件 (2025-03-07 14:12:00
),当时并没有检测到新的死锁发生。- 初步判断当前问题可能不是由新的死锁直接导致,而是由长时间的锁等待引起。
步骤 2:分析 Quartz 异常堆栈信息
- 第一次异常堆栈: 指向 Quartz 集群节点的
doCheckin
操作 (JobStoreSupport.doCheckin
)。这表明 Quartz 集群节点在尝试进行状态报告时,无法获取数据库行锁,暗示了集群管理相关的锁竞争可能是原因。 - 第二次异常堆栈: 指向手动触发 Quartz 任务 (
JobStoreSupport.storeTrigger
)。这表明在尝试手动触发任务并存储触发器信息时,无法获取数据库行锁。
步骤 3:执行 SHOW FULL PROCESSLIST
- 执行
SHOW FULL PROCESSLIST;
命令,查看当前 MySQL 服务器上正在运行的线程。 - 观察到存在多个长时间运行的
SELECT * FROM QRTZ_LOCKS WHERE ... FOR UPDATE
查询,针对STATE_ACCESS
和TRIGGER_ACCESS
锁。这些语句是 Quartz 尝试获取悲观锁的操作,长时间持有这些锁会阻塞其他需要相同锁的操作。
步骤 4:再次分析 INNODB MONITOR OUTPUT
(第二次)
- 再次执行
SHOW ENGINE INNODB STATUS\G
命令。 LATEST DETECTED DEADLOCK
部分仍然是之前的事件,没有检测到新的死锁。- 关键发现:
TRANSACTIONS
部分显示了多个长时间等待STATE_ACCESS
锁的事务,以及一个非常长时间(超过 6 小时)活跃且持有锁的事务(线程 ID6802677
)。 SEMAPHORES
部分显示了一些OS WAIT ARRAY INFO
的非零计数,可能暗示操作系统资源等待。- Buffer Pool 命中率高,IO 看起来正常。
主要发现和怀疑原因:
- 长时间未提交的事务: 线程 ID
6802677
对应的事务活跃时间极长,并持有锁,极大地阻塞了其他需要相同锁的操作。这是当前最关键的问题。 - Quartz 集群管理竞争: 多个节点或线程可能频繁地尝试获取和更新调度器状态锁。
- 高并发的任务触发: 在短时间内触发大量任务可能导致触发器相关表的锁竞争。
- 数据库性能瓶颈(可能性较低): 虽然 IO 看起来正常,但整体数据库压力仍然可能是一个因素。
QRTZ_LOCKS
和相关表的索引效率: 可能存在索引没有被有效利用的情况。- Quartz 配置不当: 例如,
clusterCheckinInterval
设置过小。
下一步行动建议:
- 立即终止长时间未提交的事务: 使用
KILL 6802677;
命令在 MySQL 客户端中执行。 - 再次检查
SHOW FULL PROCESSLIST
: 确认该事务已终止。 - 在问题再次出现时: 立即获取最新的
INNODB MONITOR OUTPUT
。 - 分析 Quartz 集群配置: 特别是
org.quartz.jobStore.clusterCheckinInterval
的设置。 - 分析 Quartz 的并发操作和任务调度策略: 了解集群节点数量、任务触发频率等。
- 监控数据库性能: 关注 CPU、内存、IO 等指标。
- 考虑优化
QRTZ_LOCKS
和相关表的索引。
总结:
本次排查过程的关键是识别出了一个长时间持有锁的事务,这很可能是导致锁等待超时的主要原因。解决这个问题后,还需要进一步分析 Quartz 的配置和并发行为,以防止类似问题再次发生。