Java中通过MySQL版本号字段实现乐观锁,核心是WHERE version = ?原子校验,需在表中添加INT型version字段(初始值0/1),DAO层UPDATE时SET version=version+1且WHERE含原version,Service层读-改-写并根据影响行数判断冲突后重试或抛异常。

Java 中通过 MySQL 的版本号字段实现业务层乐观锁,核心是利用数据库的 WHERE version = ? 条件做原子性校验,避免并发更新覆盖。它不依赖数据库锁机制,而是靠应用层重试 + 版本比对来保障数据一致性。
版本号字段怎么设计
在对应的数据表中添加一个整型字段(如 version),初始值设为 0 或 1。每次成功更新时,该字段自动 +1。推荐使用 TINYINT 或 INT 类型,注意避免溢出(一般够用)。
- 建表示例:
version INT NOT NULL DEFAULT 0 - 不要用时间戳或 UUID 做“版本号”,它们不具备单调递增和可比性
- 确保该字段在所有涉及更新的 SQL 中被显式读取和参与条件判断
DAO 层怎么写更新逻辑
执行 UPDATE 时,必须带上当前读到的 version 值作为 WHERE 条件,并在 SET 子句中将 version +1。MySQL 的 ROW_COUNT() 会返回实际影响行数,等于 1 才表示更新成功。
- 典型 SQL:
UPDATE order SET status = ?, version = version + 1 WHERE id = ? AND version = ? - MyBatis 示例:
<update id="updateWithVersion">
UPDATE order SET status = #{status}, version = version + 1
WHERE id = #{id} AND version = #{version}
</update> - JDBC 中检查
PreparedStatement.executeUpdate()返回值是否为 1
Service 层怎么处理并发冲突
业务方法需封装“读-改-写”流程,并捕获更新失败场景,决定是否重试或抛异常。
立即学习“Java免费学习笔记(深入)”;
- 先查出当前记录(含 version)
- 基于业务逻辑修改字段,但保持 version 不变(用于 WHERE)
- 调用 DAO 更新;若影响行数为 0,说明 version 已变 → 数据已被他人修改
- 可选择:立即抛
OptimisticLockException,或最多重试 N 次(重新 select 再 update)
注意事项和常见坑
乐观锁不是万能的,用错场景反而引发问题。
- 不适合高冲突场景(如秒杀库存扣减),重试开销大,应结合 Redis + Lua 或数据库行锁
- 注意事务边界:SELECT 和 UPDATE 必须在同一个事务里,否则可能读到脏数据
- 避免在 UPDATE 中漏掉 version 条件,或误把新 version 当旧值传入 WHERE
- 批量更新、关联更新等复杂操作需额外设计,不能简单套用单行 version 模式


















