- 技术笔记,巩固知识点
- 记录工作实战
一、背景
1.就像多线程更新同一临界资源,需要加CAS锁或Aync(synchronized、RentrantLock),保证数据安全
2.多个请求,修改同一条的mysql记录的某个数值,也需要加锁(mysql层次的乐观锁)
3.加同步锁:效率太低
4.不加锁:mysql 多版本控制机制,每个事务看到的都是自己的副本(就像多核cpu,每个核只能看到自己的缓存副本),会导致数据更新不一致问题
mysql 乐观锁实现
/*** 乐观锁更新库存,库存不能为负数* @param id* @param stock 入参* @return* @author Administrator* @date 2024/5/28 17:50**/public void changeStockByIdWithVersion(Integer id, BigDecimal stock) {Assert.isTrue(stock.compareTo(BigDecimal.ZERO)>=0, "更新失败:欲更新库存为负数");TailoringStockManagementEndProductStock db = getById(id);boolean update = lambdaUpdate().set(ProductStock::getPurchaseQuantity, stock).set(ProductStock::getVersion, db.getVersion() + 1).set(ProductStock::getUpdateBy, AuthUtil.getName()).set(TailoringStockManagementEndProductStock::getUpdateAt, LocalDateTime.now()).eq(ProductStock::getId, id).eq(ProductStock::getVersion, db.getVersion()).update();Assert.isTrue(update, "更新失败:乐观锁未锁中,请重新操作");}
mysql 乐观锁原理论述
并发情况,事务A在获取原库存、计算最终库、执行sql之前,这段时间内,事务B已把库存修改提交,此时事务A再更新,会覆盖掉事务B的更新。
简单说,事务A开启时,拿到数据快照,相当于拿到了库存的副本。
要想保证数据更新,必须要保证在事务A期间,没有别的事务更新。也就是version没有变化。
在实现时,只需在执行sql之前获取一下version(同一个事务内,任何时候任何地方时候获取version都是一样的,简化实现,直接紧贴在更新sql前一行),作为更新条件。根据返回的true false 判断是否命中。false则要抛异常,通知用户重试
其它简单方案
使用分布式锁,将库存的id 作为key, 同一条库存同一时间只允许一个事务更新