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

Spring Boot中@Transactional如何实现MySQL数据行的顺序独占读写?

问题原因分析
  • 你对MySQL InnoDB引擎的SERIALIZABLE隔离级别的理解存在偏差:InnoDB实现SERIALIZABLE的核心逻辑是将普通的一致性非锁定读自动转换为SELECT ... FOR SHARE(共享读锁),而非读阶段就直接加排他锁。多个并发事务可以同时持有同一行的共享读锁,因此你会看到所有事务都能同时读取到行数据。
  • 死锁和仅1个事务更新成功的原因:当持有共享读锁的事务尝试执行UPDATE操作时,需要先将共享锁升级为排他锁,升级的前提是没有其他事务持有该行的共享锁。此时所有并发事务都持有共享锁,互相等待对方释放锁,触发数据库死锁检测后,数据库会随机终止大部分事务,仅保留1个事务完成更新,因此最终计数只从1更新为2。
  • 前置更新生效的逻辑:UPDATE语句会直接申请该行的排他锁,且一旦持有排他锁,其他事务的UPDATE/加锁查询操作都会阻塞,因此事务会串行执行,计数累加结果符合预期。
不执行前置更新的实现方案

要实现读阶段就独占行访问,只需要在读数据时主动申请排他锁即可,不需要额外执行无意义的前置更新,两种常见实现方式:

  • 如果你使用Spring Data JPA:在对应的查询方法上添加@Lock(LockModeType.PESSIMISTIC_WRITE)注解,框架会自动将查询语句转为SELECT ... FOR UPDATE,读阶段就持有排他锁,其他事务的同查询操作会直接阻塞,直到当前事务提交释放锁。
    示例代码:
public interface TestRepository extends JpaRepository<TestEntity, Long> {
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    Optional<TestEntity> findById(Long id);
}
  • 如果你使用原生JDBC/MyBatis:直接将查询语句写为SELECT * FROM test_table WHERE id = ? FOR UPDATE即可达到相同效果。
注意事项
  • 该方案不需要设置SERIALIZABLE隔离级别,默认的READ COMMITTED/REPEATABLE READ隔离级别下即可生效
  • 要确保查询和更新操作在同一个事务内,否则锁会在查询结束、事务提交时提前释放
  • 要避免锁范围过大、事务执行时间过长导致的性能问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 02:24:03