乐观锁场景描述及代码实现
1.使用场景
乐观锁概念描述 | 每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据 |
---|---|
乐观锁使用场景 | 乐观锁主要是针对并发下,多读少写的场景,资源提交冲突(例如下面例子),其他使用方需要重新读取资源,会增加读的次数,但是可以面对高并发场景,前提是如果出现提交失败,用户是可以接受的。因此一般乐观锁只用在高并发、多读少写的场景。 |
2.代码实现(基于版本号方式实现)
举一个简单的例子:假设数据库中账户信息表中有一个version字段,当前值为1;而当前账户余额字段为100;
1、操作员A此时将其读出(version=1),并从其账户余额中扣除50(100-50);
2、在操作员A操作的过程中,操作员B也读入此用户信息(version=1),并从其账户余额中扣除20(100-20);
3、操作员A完成了修改操作,将数据版本号+1(version=2),连同账户扣除后余额(余额剩余50,提交至数据库更新,此时由于 提交数据库版本大于数据库当前记录的版本,数据被更新,数据库记录version更新为2
4、操作员B完成了操作,也将版本号+1(version=2)试图向数据库提交数据余额剩余80,但此时比对数据库记录版本时发现,操作员B提交的数据版本号为2,数据库记录当前版本也为2,不满足“提交版本必须大于记录当前版本才能执行更新”的乐观锁策略,因此,操作员B的提交被驳回。 这样就避免了操作员B用基于version=1的旧数据修改的结果覆盖操作员A的操作结果的可能。
…以下是一段扣除库存的代码,使用乐观锁实现实体及sql语句:
库存表: CREATE TABLE
storage
(
id
int NOT NULL AUTO_INCREMENT,
commodity_code
varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT ‘商品code’,
count
int DEFAULT ‘0’ COMMENT ‘商品数量’,
version
int DEFAULT ‘1’ COMMENT ‘版本号’,
PRIMARY KEY (id
),
UNIQUE KEYcommodity_code
(commodity_code
)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb3 COMMENT=‘库存’;
1.实现层
public DefaultResponse updateStorage(StorageDTO storageDTO) {
try {
log.info("用户"+storageDTO.getUser()+"开始扣除库存=========>");
QueryWrapper<Storage> wrapper = new QueryWrapper<>();
wrapper.lambda().eq(Storage::getCommodityCode, storageDTO.getCommodityCode());
Storage storageResult = baseMapper.selectOne(wrapper);
//如果当前订单编号存在,并且库存数量大于下单数量的时候开始扣库存
if (storageResult != null && storageResult.getCount() > storageDTO.getCount()) {
//这里用sleep主要是为了体现多个线程同时争夺扣库存
Thread.sleep(5000);
storageResult.setCount(storageResult.getCount() - storageDTO.getCount());
int i = storageMapper.upStockByCode(storageResult);
if (i > 0) {
log.info(storageDTO.getUser()+"库存扣除成功=========>");
return DefaultResponse.ok(storageDTO.getUser() + ": 库存扣除成功");
}
}
return new DefaultResponse(StatusCode.ERROR, storageDTO.getUser() + " :库存扣除失败");
} catch (Exception ex) {
return new DefaultResponse<>(StatusCode.ERROR, ex.getMessage());
}
}
2.sql层级代码
<update id="upStockByCode">
update storage set count = #{count},version = version + 1 where version = #{version} and commodity_code = #{commodityCode}
</update>