mybatis-plus 乐观锁插件

本文探讨了数据库中的悲观锁与乐观锁策略,重点讲解了version版本号实现的乐观锁,包括原理、使用场景、SQL操作、MyBatis Plus插件配置及测试案例。同时,介绍了CAS算法在乐观锁中的应用,分析了其优缺点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞,直到它拿到锁。

数据库行锁,表锁等,读锁,写锁等使用悲观锁

二 、乐观锁
  • 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会加锁,只在更新的时候会判断一下在此期间别人有没有去更新这个数据。
  • 乐观锁支持多线程并发,每个线程在不同的时间节点对数据做更新操作,每次更新时候都会判断其- 他线程是否对数据做了更新。
1、version版本号实现
①、version版本号机制
  • 一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。

  • 当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。

②、数据库表结构,

注意需要设置默认值为 1 (这个时mybatis-plus默认值,自己实现可以为其他)

在这里插入图片描述

③、sql源码

下面有详细测试代码

  // 数据库版本 根据你的 id 查询出来的 version 
 // 执行更新,此时 user.version = 1 ,数据库版本为 1,当前数据库版本为 2 , 2 != 1 更新失败
 update set user(name,version) values(user=#{user.name},version=version+1) where id=#{user.id} and version=#{user.version}
④、实体类

在这里插入图片描述

⑥、配置乐观锁插件

选用一种即可

// Spring Boot 方式
@Configuration
@MapperScan("按需修改")
public class MybatisPlusConfig {
    /**
     * 旧版
     */
    @Bean
    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
        return new OptimisticLockerInterceptor();
    }
    
    /**
     * 新版
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}
⑦、测试

模拟两个事务,事务一修改数据未提交、事务二执行插入(乐观锁插件本质

    @Test
    public void testLock(){
    	// select name,version from user;  version = 1
        User user = userMapper.selectById(2L);
        user.setName("ye凌");
        // select name,version from user;  version = 1
        User user2 =  userMapper.selectById(2L);
        user2.setName("椰林阁");
        // 执行更新,此时 user2.version = version = 1 ,更新成功 version = 2 
        // update set user(name,version) values(user=#{user.name},version=version+1) where id=#{user.id} and version=#{user.version}
        userMapper.updateById(user2);
        // 数据库版本 根据你的 id 查询出来的 version 
        // 执行更新,此时 user.version = 1 ,数据库版本为 1,当前数据库版本为 2 , 2 != 1 更新失败
        // update set user(name,version) values(user=#{user.name},version=version+1) where id=#{user.id} and version=#{user.version}
        userMapper.updateById(user);
    }
⑧、缺点

循环开销大

我们知道乐观锁在进行写操作的时候会判断是否能够写入成功,如果写入不成功将触发等待 -> 重试机制,这种情况是一个自旋锁,简单来说就是适用于短期内获取不到,进行等待重试的锁,它不适用于长期获取不到锁的情况,另外,自旋循环对于性能开销比较大。

2、CAS 算法
①、原理
  • CAS(compare and swap) 比较并交换,有三个操作数,内存地址V ,预期值B,要替换得到的目标子A。

  • CAS指令执行时,比较内存地址V与预期值B是否相等,若相等则将A赋给B,(不相等则会循环比较直到相等)整个比较赋值操作是一个原子操作。

②、CAS缺点
  • (1)循环时间开销大:当内存地址V与预期值B不相等时会一直循环比较直到相等;

  • (2)只能保证一个共享变量的原子操作;

③、存在问题

只能保证数据一致,不能保证数据未被修改,存在 ABA 问题

如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那么我们就能说明它的值没有被其他线程修改过吗?很明显不是,因为在这段时间内它的值可能被改为其他值,然后又被改回A,那CAS操作就会认为它从来没被改过,这个问题就被称为 CAS 操作的“ABA” 问题;

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值