乐观锁在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的条件不成立
}
}
