登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  数据库 >  MySQL

MySQL performance_schema events_transactions_current 怎样抓住长事务

来源:17golang原创

时间:2026-11-14 03:25:12 186浏览 收藏

线上出现锁等待时,先不要把所有慢 SQL 都当成根因。对“事务还没结束、已经持续很久”的判断,MySQL Performance Schema 已经给了一个直接入口:performance_schema.events_transactions_current。筛选 STATE = 'ACTIVE'、END_EVENT_ID IS NULL,再把 TIMER_WAIT 从皮秒换算成秒,就能先抓住当前仍在运行的长事务。

本文只讨论当前事务、长事务和锁诊断,不把历史事务表当成实时列表,也不把事务持续时间直接等同于锁等待时间。

先确认当前事务事件确实在采集

events_transactions_current 按线程保存最近一个被监控的当前事务事件,每个线程最多对应一行。若事务 instrument 没开,或者 consumer 没开,查询为空并不代表系统没有事务。

-- 先查看事务采集开关,避免把“未采集”误判成“没有长事务”
SELECT NAME, ENABLED, TIMED
FROM performance_schema.setup_instruments
WHERE NAME = 'transaction';

-- current consumer 决定是否维护当前事务表
SELECT NAME, ENABLED
FROM performance_schema.setup_consumers
WHERE NAME = 'events_transactions_current';

-- 仅在确认允许动态调整采集时执行;TIMED=YES 才能获得耗时字段
UPDATE performance_schema.setup_instruments
SET ENABLED = 'YES', TIMED = 'YES'
WHERE NAME = 'transaction';

UPDATE performance_schema.setup_consumers
SET ENABLED = 'YES'
WHERE NAME = 'events_transactions_current';
MySQL 事务采集开关、当前事务表和耗时字段的静态关系说明图
图1:结构说明图,展示 transaction instrument、current consumer 与当前事务耗时字段的关系;这是静态说明图,不是运行截图。

生产环境调整前要评估采集开关的影响,并确认账号有访问 Performance Schema 设置表的权限。文章中的 UPDATE 是配置示例,不代表已经在你的实例执行。

用 TIMER_WAIT 抓仍然活跃的长事务

MySQL 8.4 文档说明,当前事件尚未结束时,END_EVENT_ID 为 NULL,TIMER_WAIT 表示从开始到当前时刻经过的时间,单位是皮秒。下面用 300 秒作为排查阈值,你可以按业务事务的正常时长调整。

-- 300 秒 = 300 × 10^12 皮秒;只看仍处于 ACTIVE 的当前事务
SELECT
    THREAD_ID,
    EVENT_ID,
    STATE,
    ROUND(TIMER_WAIT / 1000000000000, 1) AS elapsed_seconds,
    ACCESS_MODE,
    ISOLATION_LEVEL,
    AUTOCOMMIT,
    NESTING_EVENT_ID,
    NESTING_EVENT_TYPE
FROM performance_schema.events_transactions_current
WHERE END_EVENT_ID IS NULL
  AND STATE = 'ACTIVE'
  AND TIMER_WAIT IS NOT NULL
  AND TIMER_WAIT >= 300 * 1000000000000
ORDER BY TIMER_WAIT DESC;

这里的三个条件各有作用:END_EVENT_ID IS NULL 排除已经结束但仍留在当前表的事件;STATE = 'ACTIVE' 把关注点放在尚未提交或回滚的事务;TIMER_WAIT IS NOT NULL 避免未计时的事件进入比较。若结果为空,先回到上一节检查采集开关和查询权限。

别把事务时间和锁等待时间混为一谈

长事务只是“事务保持活动的时间很长”,它可能在等待业务输入、持有锁、执行慢查询,或者只是应用忘记提交。要判断它是否正在阻塞别人,需要把事务线程与 performance_schema.data_lock_waits 关联。

-- requesting 是等待锁的一方,blocking 是持有阻塞锁的一方
SELECT
    w.REQUESTING_THREAD_ID,
    w.BLOCKING_THREAD_ID,
    ROUND(r.TIMER_WAIT / 1000000000000, 1) AS requesting_seconds,
    ROUND(b.TIMER_WAIT / 1000000000000, 1) AS blocking_seconds,
    r.STATE AS requesting_state,
    b.STATE AS blocking_state,
    r.ACCESS_MODE AS requesting_mode,
    b.ACCESS_MODE AS blocking_mode
FROM performance_schema.data_lock_waits AS w
LEFT JOIN performance_schema.events_transactions_current AS r
  ON r.THREAD_ID = w.REQUESTING_THREAD_ID
LEFT JOIN performance_schema.events_transactions_current AS b
  ON b.THREAD_ID = w.BLOCKING_THREAD_ID
WHERE r.END_EVENT_ID IS NULL
   OR b.END_EVENT_ID IS NULL;
MySQL data_lock_waits 连接 requesting 事务与 blocking 事务的静态关系说明图
图2:关系说明图,展示 data_lock_waits 如何连接等待线程与阻塞线程,并回到各自的当前事务属性;这是静态说明图,不是运行截图。

如果 BLOCKING_THREAD_ID 对应的事务持续时间很长,同时又出现在多个等待关系中,它更值得优先处理。但不要仅凭这张表直接终止会话:先结合业务确认是否处在必须原子完成的流程,再决定提交、回滚或让应用修复事务边界。

需要更具体的引擎信息时再看 InnoDB 状态

events_transactions_current 适合回答“哪个线程有一笔仍活跃的事务、已经持续多久、事务属性是什么”。当问题进一步变成“具体锁住了哪个索引记录、出现了什么 InnoDB 锁描述”,可以补充:

-- 该命令需要 PROCESS 权限;它提供 InnoDB Monitor 的引擎侧状态
SHOW ENGINE INNODB STATUS;

它们的分工可以这样记:当前事务表负责按线程和耗时排序,data_lock_waits 负责看等待与阻塞关系,InnoDB 状态负责补充引擎侧细节。三者不是同一份数据,也不能互相替代。

常见误区与速查结论

  • 查询 events_transactions_history 只能看到已经结束的近期事务,不能代替 current 表抓实时长事务。
  • TIMER_WAIT 是皮秒,不是秒;换算时使用 1000000000000,不要直接和 300 比较。
  • 事务很长不必然代表它正在阻塞别人;是否阻塞要看 data_lock_waits 的 blocking 关系。
  • 事务事件边界和服务器事务状态基本一致,但只访问非事务表的语句不会让 Performance Schema 事务事件进入同一事务边界。

排查顺序可以压缩成一句话:先确认采集,再用 END_EVENT_ID、STATE 和 TIMER_WAIT 找出活动长事务,随后用线程 ID 关联锁等待,最后用 InnoDB 状态补足记录级信息。

相关问题

为什么 TIMER_WAIT 是 NULL?

通常是对应 instrument 没有开启计时,即 TIMED 不是 YES。先查看 setup_instruments,不要把 NULL 当成零秒。

events_transactions_current 为什么看不到我刚执行的事务?

检查 Performance Schema 是否启用、transaction instrument 和 current consumer 是否开启,并确认事务确实访问了事务性存储引擎。自动提交模式下,每条使用事务性引擎的语句也可能形成很短的隐式事务。

长事务应该立刻 KILL 吗?

不建议只按时长自动处理。先确认它是否是阻塞链中的 blocking 方、是否处在可安全回滚的业务阶段,再由业务负责人决定提交、回滚、终止会话或修复连接池的事务边界。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>