乐观锁
wenking 12/13/2023
# 乐观锁
乐观锁通常会使用版本号机制或者 CAS 算法来实现。通常是在建表时,新增一个 version 版本号字段, 假设初始值为 1。当多线程修改某条记录时,会先进行查询操作(包括版本号 version),后续修改数据时, 会判断查询出来的当前版本号是否与数据库中的一致,一致才会进行更新,并且对 version 版本号进行加 1 操作, 否则更新失败。
适用场景:
- 读多写少(读数据多写数据少)的场景
- 并发事务导致的更新丢失(第一类更新丢失【回滚覆盖】和第二类更新丢失【更新覆盖】)
- 库存修改(乐观锁的变形,本质一样【判断条件为 stock > 0 而非 version = curVersion】)
# 配置乐观锁插件
注意:考虑到项目中可能会添加多个插件,需要注意顺序关系,官方推荐顺序如下:
多租户插件 -> 动态表名插件 -> 分页插件 -> 乐观锁插件 -> sql 性能规范插件 -> 防止全表更新与删除插件。
@Configuration
@MapperScan("com.king.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 实体类
实体类中,通过@Version
指定锁字段
@Data
public class SkuStock implements Serializable {
private Integer id;
private Integer productId;
private Integer stock;
@Version
private Integer version;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注意事项:
- @Version 注解仅支持数据类型为:int, Integer, long, Long, Date, Timestamp, LocalDateTime
- 整数类型下会执行 newVersion = oldVersion + 1
- newVersion 会回写到 entity 实体类中
- 仅支持 updateById(id) 与 update(entity, wrapper) 方法
- 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!