乐观锁版本号用于校验数据一致性而非加锁,需在表中添加INT/BIGINT类型version字段(默认值1),配合UPDATE时WHERE version=?和version=version+1实现;失败返回0须业务层捕获并重试或报错。

乐观锁版本号(Version)不是用来“锁住”数据的,而是用来“验明正身”的——它在更新时确认:你操作的数据,还是你读到的那一条吗?核心不在加锁,而在校验;不靠阻塞,而靠判断。
版本号字段怎么设才合理
数据库表中需新增 version 字段,类型推荐 INT 或 BIGINT,默认值设为 1(非 0),避免初始更新误判。不要用时间戳替代 version,因为毫秒级精度在高并发下仍可能重复,且无法体现逻辑更新次数。
- MySQL 示例:
ALTER TABLE order_info ADD COLUMN version INT DEFAULT 1 COMMENT '乐观锁版本号'; - 字段必须允许为 NULL 吗?不需要——明确设默认值,避免空值导致 WHERE 条件失效
- 是否需要加索引?一般不用单独建索引,但若该字段常用于高频更新 + 查询组合条件,可考虑联合索引
@Version 注解必须精准标注
MyBatis-Plus 仅识别被 @Version 显式标记的字段作为乐观锁依据。它不依赖字段名(比如叫 “ver” 或 “rev” 就无效),也不支持多个 @Version 字段。
- 正确写法:
@Version private Integer version; - 错误写法:字段名为
optLock却没加注解;或同时标注两个@Version字段 - Lombok 的
@Data不影响 @Version 生效,但确保编译后 getter/setter 存在(尤其使用 MapStruct 等工具时)
更新失败后该怎么响应
MyBatis-Plus 执行 update 时若 version 不匹配,不会抛异常,而是返回 0(影响行数为 0)。这是关键信号,必须由业务代码主动捕获并决策。
- 常见做法是重试:先重新 select 最新数据(含新 version),再构造新更新请求
- 建议限制最大重试次数(如 3 次),避免雪崩;可配合简单退避(如 sleep 10ms)
- 对强一致性要求高的场景(如支付状态变更),失败应直接返回「操作已被其他用户提交」,而非静默重试
它防不住哪些“绕过”行为
乐观锁只对走 MyBatis-Plus update 方法的请求生效。任何绕过 ORM 的操作,都会跳过 version 校验:
- 原生 SQL(
update ... set status='paid' where id=123)不带 version 条件 - 数据库客户端直连执行更新
- 其他服务或定时任务未集成乐观锁逻辑
- 解决方案:关键表加数据库层触发器拦截非法更新,或统一通过 API 网关/仓储层封装写操作

















