Skip to content

MySQL 中长事务可能会导致哪些问题? ​

  • 标签:后端、MySQL、数据库

🏛 业务架构师 ​

长事务是数据库的"隐形杀手",我从业务后果倒推讲:

  • undo 版本链变长,MVCC 变慢:长事务持有着几小时前的 ReadView,它可能读到的所有旧版本都不能清理,undo log 膨胀,全库查询都要在长版本链上找数据,整库性能被一个事务拖垮;
  • 锁与死锁高发:行锁、间隙锁持有越久,并发冲突概率越大,交易高峰期表现为大量锁等待甚至批量超时;
  • 连接资源占用:大事务占着连接不走,连接池被打满,其他业务请求连不上库,故障从单点扩散到全站;
  • 主从延迟:从库回放大事务耗时长,读写分离后读到的旧数据变多,客诉上升。

业务上的典型根因我见得最多的是:事务方法里夹了 RPC 调用、MQ 发送、甚至人工等待——比如 @Transactional 方法里同步调用风控接口,风控慢 5 秒,数据库锁就多持有 5 秒。这本质是把外部依赖的不确定性灌进了数据库。

治理动作三条:监控 information_schema.innodb_trx 找出长事务并告警;代码规约"事务里只做 DB 操作,外部调用移到事务外(或事务提交后回调)";大批量任务改成分批小事务提交。长事务治理是稳定性投入里 ROI 最高的一类,因为它防的是全站性故障。

🏗 系统架构师 ​

长事务是我做 MySQL 治理时的重点对象,危害按层次:

  • 并发与锁:事务越长持有行锁/间隙锁越久,阻塞上游写入,锁等待链式传播,极端时拖垮整库连接数;
  • MVCC 与回滚段:长事务的 ReadView 长期存在,undo 版本链无法 purge,undo 表空间膨胀,其他事务反向扫描版本链变慢——这是"一个报表事务拖慢全库"的经典事故;
  • 主从延迟:大事务 binlog 一次传输,从库单线程回放耗时,延迟突增;
  • 连接资源:事务挂着连接不放,连接池被打满,全站雪崩。

治理手段:

  1. information_schema.innodb_trx + performance_schema 监控 trx_started,超过阈值(如 30s)告警甚至 kill;
  2. 开发规范:事务里不放 RPC/慢查询/用户交互,"事务内只做 DB 操作、能拆则拆",大任务改分批提交(每批 500~1000 行);
  3. set autocommit=1,避免隐式长事务;必要时 SET SESSION MAX_EXECUTION_TIME 兜底。

💻 全栈开发专家 ​

长事务(运行时间几秒到几分钟的事务)的杀伤面很广,我按实际影响列:

  1. undo log 膨胀:MVCC 旧版本不能清理,undo 表空间涨,连带整个系统回滚段压力大。
  2. 锁持有久:行锁/间隙锁一直不放,别的事务排队,连接池耗尽,接口大面积超时。
  3. 主从延迟:从库 SQL 线程重放这个大事务同样慢,且是单事务串行,延迟突然跳涨的元凶常是它。
  4. 占用连接:业务高峰时少量长事务能把连接池拖死。 5 回滚代价巨大:百万行事务回滚时间可能是执行的好几倍。

排查:

sql
SELECT trx_id, trx_started, trx_mysql_thread_id, trx_rows_modified
FROM information_schema.innodb_trx
ORDER BY trx_started;   -- trx_started 超过 60s 就要关注

配合 performance_schema.events_transactions_current 看未提交语句。

根治手段:把 RPC 调用、文件上传、发短信挪出事务(这是最常见根因);大批量任务分批提交(每 500 条一提交);框架层面用 @Transactional(timeout=...) 加保险丝。我定过军规:事务里只做 DB 操作。