java分布式事务需协同分布式锁与数据库事务:锁确保请求串行化执行,事务保证原子提交,二者职责分离;须用可重入+自动续期锁(如redisson)、db层配合乐观/悲观锁,并设超时与异常兜底机制。

在Java事务管理中,单纯依赖数据库本地事务无法解决跨服务、跨节点的并发冲突问题。要实现强一致性,必须将分布式锁与数据库事务协同设计——锁负责资源互斥,事务负责原子提交,二者缺一不可。
锁和事务的职责必须严格分离
分布式锁不是替代事务,而是为事务创造安全执行的前提。比如库存扣减场景:多个服务实例同时处理同一商品订单,若不加锁,即使每个实例内部用@Transactional保证本地ACID,仍会因并发读写导致超卖。锁的作用是让请求串行化进入事务边界。
- 先获取分布式锁(如Redisson可重入锁),超时时间需大于事务最大执行时间
- 锁获取成功后,再开启数据库事务(@Transactional或手动控制)
- 事务内完成所有DB操作,包括校验、更新、日志记录
- 事务提交成功后,再释放锁(注意:不能在事务内释放,避免锁提前失效)
必须使用可重入且带自动续期的分布式锁
普通SETNX+EXPIRE存在锁过期但业务未完成的风险,导致其他节点误入。生产环境应选用支持看门狗机制的方案:
- Redisson的RLock默认开启自动续期,只要线程存活就刷新过期时间
- ZooKeeper临时顺序节点天然具备会话绑定特性,崩溃即释放
- 避免自己手写Lua脚本实现,容易遗漏原子性或误删他人锁
数据库事务需配合乐观/悲观锁做二次防护
分布式锁解决的是“谁先执行”,而DB层需防止“执行中被覆盖”。两者形成双保险:
- 高并发读多写少场景:用@Version字段实现乐观锁,update时校验version,失败则重试整个事务流程
- 写竞争激烈场景:在select时加SELECT FOR UPDATE(InnoDB行锁),确保后续update基于最新快照
- 避免仅靠锁不校验业务状态,例如扣库存前必须查剩余量,不能只依赖锁保证串行
异常路径必须统一兜底,防止锁残留或事务悬挂
网络超时、JVM崩溃、OOM等都可能导致锁未释放或事务未提交。关键防护点:
- 锁设置合理超时(如30秒),并配置Redisson的lockWatchdogTimeout
- 事务方法标注timeout属性(如@Transaction(timeout = 20)),超时自动回滚
- 业务代码中catch所有异常,确保finally块里调用unlock()(Redisson已封装tryLock+lockInterruptibly的安全用法)
- 引入定时巡检任务,扫描长时间持有的锁并告警,必要时人工干预
这种组合不是简单叠加,而是分层防御:分布式锁划出安全执行窗口,数据库事务保证窗口内操作的原子性,业务逻辑校验兜住最终语义。三者对齐,才能在分布式环境下逼近强一致性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











