核心是让事务仅保障关键DB写操作的原子性,剥离非DB、跨服务动作;用TransactionTemplate精准控制范围、设超时、查写分离、分批提交、最终一致性替代分布式事务。

避免大事务拖垮数据库性能,核心是让事务只做它该做的事:保障几行关键数据库写操作的原子性,其余一切耗时、非DB、跨服务动作全部剥离出去。
只包裹真正需要回滚的DB写操作
事务不是“功能开关”,而是“保护罩”。它只该罩住那些一旦失败就必须全部撤销的数据库变更。
- 订单创建:仅包含 扣减库存 + 插入订单主表 + 更新用户余额 这三步SQL;发券、通知、积分变动全部移出事务,用异步消息或定时任务补偿
- 用户注册:只包 插入用户表 + 初始化权限配置;欢迎邮件、ES同步、风控调用都放在事务提交之后
- 别在 @Transactional 方法里混着 feignClient.call()、redisTemplate.set()、Thread.sleep(100)、文件IO —— 这些不参与ACID,却会死死占着数据库连接
用 TransactionTemplate 精准控制事务范围
@Transactional 是“整方法级”的粗粒度控制,容易把查询、计算、校验全裹进去。TransactionTemplate 能把事务缩到几行SQL内,真正实现“最小必要”。
- 先查缓存或DB获取数据(事务外)
- 再用 transactionTemplate.execute(status → { saveOrder(); updateStock(); }) 执行纯写操作
- 最后发MQ、更新Redis、记录日志(事务外)
- 务必设置超时:transactionTemplate.setTimeout(30),防止单条SQL卡死导致连接长期占用
查询坚决不进事务,除非真需要锁
SELECT 不改数据,不需要事务一致性保障。放进事务只会延长锁持有时间、抬高死锁概率、拖慢整个事务生命周期。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
立即学习“Java免费学习笔记(深入)”;
- 反模式:@Transactional 方法里先 select * from order where user_id=?,再 insert into order_item —— 查完再写,事务已开太久
- 正确做法:把 select 提前到事务方法外;若需读写一致(如“查余额→扣款”),只对关键行用 SELECT ... FOR UPDATE 显式加锁,且锁范围越小越好
- 避免在事务中做 count(*)、sum() 等聚合查询,尤其面对大表时
批量操作必须分批提交
一次性处理 10 万条记录?哪怕只用一个 @Transactional,也会让 undo log 疯长、purge 卡死、磁盘告急。
- Java 层强制切片:每批 500~2000 行,每批单独开启并提交事务
- 禁用“一把梭”写法:不要用 @Transactional 包整个 for 循环;改用 TransactionTemplate 控制每批的 begin/commit
- JPA 用户注意:hibernate.jdbc.batch_size 只优化 JDBC 批处理效率,不能替代事务拆分 —— 它不解决长事务问题
- 上线前加审计:测试环境拦截影响行数 > 5000 或执行时间 > 30 秒的 UPDATE/DELETE
跨服务改用最终一致性
在订单服务里 @Transactional 包裹调用库存服务,本质是伪分布式事务。网络抖动、下游重启都会让本地事务悬停,既无法提交也无法回滚。
- 订单服务落库成功后,立即发 “扣减库存” 消息;库存服务消费后执行本地事务
- 失败场景走补偿:比如库存不足,发“订单取消”消息,驱动订单服务回滚
- 不依赖两阶段提交(2PC)或 TCC,优先用可靠消息 + 本地事务表 / Saga 模式


















