乐观锁面向场景
一件商品,成本价是80元,售价是100元。老板先是通知小李,说你去把商品价格增加50元。小李正在玩游戏,耽搁了一个小时。正好一个小时后,老板觉得商品价格增加到150元,价格太高,可能会影响销量。又通知小王,你把商品价格降低30元。
此时,小李和小王同时操作商品后台系统。小李操作的时候,系统先取出商品价格100元;小王也在操作,取出的商品价格也是100元。小李将价格加了50元,并将100+50=150元存入了数据库;小王将商品减了30元,并将100-30=70元存入了数据库。是的,如果没有锁,小李的操作就完全被小王的覆盖了。
现在商品价格是70元,比成本价低10元。几分钟后,这个商品很快出售了1千多件商品,老板亏1多万。。。
乐观锁与悲观锁
- 上面的故事,如果是乐观锁,小王保存价格前,会检查下价格是否被人修改过了。如果被修改过了,则重新取出的被修改后的价格,150元,这样他会将120元存入数据库。
- 如果是悲观锁,小李取出数据后,小王只能等小李操作完之后,才能对价格进行操作,也会保证最终的价格是120元。
接下来是基于SpringBoot+MP来完成乐观锁的搭配使用。
模拟冲突
数据库中增加商品表
CREATE TABLE product (
id BIGINT ( 20 ) NOT NULL COMMENT '主键ID',
name VARCHAR ( 30 ) NULL DEFAULT NULL COMMENT '商品名称',
price INT ( 11 ) DEFAULT 0 COMMENT '价格',
version INT ( 11 ) DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY ( id )
);
添加数据
INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
实体类
@Data
public class Product {
private Long id;
private String name;
private Integer price;
private Integer version;
}
Mapper接口类
@Repository
public interface ProductMapper extends BaseMapper<Product> {
}
测试,模拟的是开头讲到的场景
@SpringBootTest
public class ProductMapperTest {
@Autowired
private ProductMapper productMapper;
@Test
void testConcurrentUpdate() {
//1、小李
Product p1 = productMapper.selectById(1L);
System.out.println(p1.getPrice());
//2、小王
Product p2 = productMapper.selectById(1L);
System.out.println(p2.getPrice());
//3、小李将价格加了50元,存入了数据库
p1.setPrice(p1.getPrice() + 50);
productMapper.updateById(p1);
//4、小王将商品减了30元,存入了数据库
p2.setPrice(p2.getPrice() - 30);
int result = productMapper.updateById(p2);
if (result == 0) {//更新失败,重试
//重新获取数据
p2 = productMapper.selectById(1L);
//更新
p2.setPrice(p2.getPrice() - 30);
productMapper.updateById(p2);
}
//最后的结果
Product p3 = productMapper.selectById(1L);
System.out.println("最后的结果:" + p3.getPrice());
}
}
解决方案(乐观锁注入)
原理
数据库中添加version字段
取出记录时,获取当前version
SELECT id,`name`,price,`version` FROM product WHERE id=1
更新时,version + 1,如果where语句中的version版本不对,则更新失败
UPDATE product SET price=price+50, `version`=`version` + 1 WHERE id=1 AND `version`=1
实现
实体类更新
对version字段添加@Version
注解
@Data
public class Product {
private Long id;
private String name;
private Integer price;
@Version
private Integer version;
}
进行一系列配置
创建包config
,创建配置类MybatisPlusConfig
注意:此时拥有了删除配置类,就可以将启动类中的 @MapperScan
扫描注解 移动到这边。
然后注入乐观锁的Bean组件
@Configuration
@MapperScan("com.xn2001.mapper")
public class MybatisPlusConfig {
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
}
回到原来的测试中继续测试,将数据库70重新改为100,运行测试类。
版权属于:乐心湖's Blog
本文链接:https://xn2001.com/archives/512.html
声明:博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!