You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring @Transactional未提交问题:长事务引发MySQL超时疑问

问题描述

工作中遇到长事务问题,该长事务导致更新查询触发MySqlTimeoutException。出现问题的查询语句如下:

-- fileDAO.updateFile
-- file_id 是主键。related_board_id 列无索引,类型为int。

UPDATE file SET related_board_id = 1 WHERE file_id = 1

对应的问题代码如下:

@Service
public class FileService {
    @Autowired
    private FileDAO fileDAO;
    
    // 已配置PlatformTransactionManager bean。Transactional使用默认设置。
    @Transactional
    public void saveFileList(List<File> fileList) {
        for (File file: fileList) {
            fileDAO.updateFile(file);
        }
    }
}

我们使用Spring 4.3.x版本,当时正在存储1200个文件,未生成该事务的错误日志,但事务未提交。有两个疑问:

  1. 为何会出现长事务?
  2. 若长事务原因是保存过多文件,为何事务未提交且无报错?
问题解答

一、长事务产生的原因

  • 单事务批量执行更新:saveFileList方法被@Transactional标记,1200次updateFile操作全部在同一个事务中执行。每次更新都会占用数据库连接,事务持续时间随操作次数线性增加,最终超出MySQL的超时阈值,触发MySqlTimeoutException。
  • 默认事务属性的影响:Spring 4.3.x中@Transactional默认传播行为为REQUIRED,隔离级别取数据库默认(通常是REPEATABLE READ)。该隔离级别下事务会持有锁直到结束,加上1200次循环的累积耗时,进一步拉长了事务执行时间。

二、事务未提交且无报错的原因

  • 异常未被捕获或日志未记录:如果某次更新触发超时异常,但异常被Spring事务管理器包裹,或者你的日志配置未记录事务相关异常,就会看不到报错日志。此时若异常未被方法内部捕获,Spring会自动回滚事务,你看到的“未提交”实际是事务被回滚。
  • 数据库主动终止事务:当事务执行时间超过MySQL的wait_timeout或innodb_lock_wait_timeout设置时,数据库会主动断开连接或终止事务。这种场景下应用端可能无法及时捕获到异常,导致事务既没提交,也没有报错日志输出。
  • 异常被隐性吞掉:如果循环中某步操作抛出异常但被代码中的try-catch块吞掉,事务会因方法提前终止而无法执行到提交逻辑,最终数据库会因连接超时或事务超时强制回滚事务,且不会在业务日志中留下记录。

内容的提问来源于stack exchange,提问作者changuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 20:38:22