9. 乐观锁

文章讲述了在并发情况下如何保证数据一致性,通过商品系统实例介绍乐观锁和悲观锁的工作原理,并展示了如何在MyBatisPlus中利用乐观锁机制解决数据冲突。作者通过模拟并发场景和配置MyBatisPlus插件展示了乐观锁在实际开发中的应用。
摘要由CSDN通过智能技术生成

当程序中出现并发访问时,就需要保证数据的一致性。以商品系统为例,现在有两个管理员均想对同一件售价为 100 元的商品进行修改,A 管理员正准备将商品售价改为 150 元,但此时出现了网络问题,导致 A 管理员的操作陷入了等待状态;此时 B 管理员也进行修改,将商品售价改为了 200 元,修改完成后 B 管理员退出了系统,此时 A 管理员的操作也生效了,这样便使得 A 管理员的操作直接覆盖了 B 管理员的操作,B 管理员后续再进行查询时会发现商品售价变为了 150 元,这样的情况是绝对不允许发生的。
要想解决这一问题,可以给数据表加锁,常见的方式有两种:

  1. 乐观锁
  2. 悲观锁

悲观锁认为并发情况一定会发生,所以在某条数据被修改时,为了避免其它人修改,会直接对数据表进行加锁,它依靠的是数据库本身提供的锁机制(表锁、行锁、读锁、写锁)。
而乐观锁则相反,它认为数据产生冲突的情况一般不会发生,所以在修改数据的时候并不会对数据表进行加锁的操作,而是在提交数据时进行校验,判断提交上来的数据是否会发生冲突,如果发生冲突,则提示用户重新进行操作,一般的实现方式为 设置版本号字段 。
就以商品售价为例,在该表中设置一个版本号字段,让其初始为 1,此时 A 管理员和 B 管理员同时需要修改售价,它们会先读取到数据表中的内容,此时两个管理员读取到的版本号都为 1,此时 B 管理员的操作先生效了,它就会将当前数据表中对应数据的版本号与最开始读取到的版本号作一个比对,发现没有变化,于是修改就生效了,此时版本号加 1。
而 A 管理员马上也提交了修改操作,但是此时的版本号为 2,与最开始读取到的版本号并不对应,这就说明数据发生了冲突,此时应该提示 A 管理员操作失败,并让 A 管理员重新查询一次数据。
在这里插入图片描述

乐观锁的优势在于采取了更加宽松的加锁机制,能够提高程序的吞吐量,适用于读操作多的场景。
那么接下来我们就来模拟这一过程。

1.创建一张新的数据表
create table shop(
	id bigint(20) not null auto_increment,
  name varchar(30) not null,
  price int(11) default 0,
  version int(11) default 1,
  primary key(id)
);

insert into shop(id,name,price) values(1,'笔记本电脑',8000);
2.创建实体类
@Data
public class Shop {

    private Long id;
    private String name;
    private Integer price;
    private Integer version;
}
3.创建对应的 Mapper 接口
public interface ShopMapper extends BaseMapper<Shop> {
}
4.编写测试代码
	/**
     * 模拟并发场景
     */
    @Test
    void hh2() {
        // A、B管理员读取数据
        Shop a = shopMapper.selectById(1L);
        Shop b = shopMapper.selectById(1L);
        // B管理员先修改
        b.setPrice(9000);
        int result = shopMapper.updateById(b);
        if (result == 1) {
            System.out.println("B管理员修改成功!");
        } else {
            System.out.println("B管理员修改失败!");
        }
        // A管理员后修改
        a.setPrice(8500);
        int result2 = shopMapper.updateById(a);
        if (result2 == 1) {
            System.out.println("A管理员修改成功!");
        } else {
            System.out.println("A管理员修改失败!");
        }
        // 最后查询
        System.out.println(shopMapper.selectById(1L));
    }

执行结果:
在这里插入图片描述

问题出现了,B 管理员的操作被 A 管理员覆盖,那么该如何解决这一问题呢?
MyBatisPlus 提供了乐观锁机制,只需要在实体类中使用 @Version 声明版本号属性:

@Data
public class Shop {

   private Long id;
   private String name;
   private Integer price;
   @Version // 声明版本号属性
   private Integer version;
}
  • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  • 整数类型下 newVersion = oldVersion + 1
  • newVersion 会回写到 entity 中
  • 仅支持 updateById(id) 与 update(entity, wrapper) 方法
  • 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!

然后注册乐观锁插件:

@Configuration
public class MyBatisConfig {
	/**
     * 旧版
     */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }


    /**
     * 注册插件(新版)
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        // 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

重新执行测试代码,结果如下:
在这里插入图片描述

  • 9
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我超爱写bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值