乐观锁在Mybatis修改中的应用
乐观锁在Mybatis修改中的应用
1.乐观锁的概念
乐观锁假设数据一般情况不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果冲突,则返回给用户异常信息,让用户决定如何去做。乐观锁适用于读多写少的场景,这样可以提高程序的吞吐量。 乐观锁采取了更加宽松的加锁机制。也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。 简单来说,乐观锁主要解决的问题是当要更新一条记录的时候,希望这条记录没有被别人更新。
2.乐观锁的实现
CAS 实现:Java 中java.util.concurrent.atomic包下面的原子变量使用了乐观锁的一种 CAS 实现方式。
版本号控制:一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会 +1。当线程 A 要更新数据时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值与当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。
具体问题应用
这里我们分析一个可能在项目中遇到的”秒杀“的问题,假如有100个商品或者票在出售,为了能保证每个商品或者票只能被一个人购买,如何保证不会出现超买或者重复卖.此时我们就可以考虑加锁的实现。这里我们就采用版本号控制的方式 注意:我们使用的这种方式针对于小型企业的解决方案,因为数据库本身的性能就是个瓶颈,如果对其并发量超过2000以上的就需要考虑其他的解决方案了
以下是具体的代码测试: 在实体类中定义出version这个字段,在配置类中配置乐观锁的拦截器,在测试类中模拟多个用户同时的修改操作
package com.qcw.domain; @Data //@TableName("tb_user") public class User { //@TableId(type = IdType.ASSIGN_ID) private Long id; private String name; @TableField(value = "pwd",select = false) private String password; private Integer age; private String tel; @TableField(exist = false) private Integer online; //逻辑删除字段,标记当前记录是否被删除 //@TableLogic(value = "0",delval = "1") private Integer deleted; @Version private Integer version; }
package com.qcw.config; @Configuration public class MpConfig { @Bean public MybatisPlusInterceptor mpInterceptor(){ //1.定义Mp拦截器 MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor(); //2.添加具体拦截器 mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor()); //3.添加乐观锁的拦截器 mpInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mpInterceptor; } }
package com.qcw; import com.qcw.dao.UserDao; import com.qcw.domain.User; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; @SpringBootTest class Mybatisplus03DmlApplicationTests { @Resource private UserDao userDao; @Test void testUpdate(){ //1.模拟两个用户的修改 User user = userDao.selectById(3L); //version=3 User user2 = userDao.selectById(3L);//version=3 user2.setName("wlx a"); userDao.updateById(user2); //version=4 user.setName("wlx b"); userDao.updateById(user); //到这里version=3的条件不成立 } }