分布式锁相关
超卖问题的解决
现象
- 一件商品,AB同时下单,扣减库存出了问题。A读到库存是1,B读到库存是1;AB同时更新数据库,更新为0,数据库中虽然是0了,但是产生了两笔订单。
- 解决办法:下沉到数据库扣减库存:
- 通过UPDATE语句,更新增量,通过UPDATE计算库存,用到了UPDATE行锁,其他的UPDATE不可以更新,需要等待。
- 解决办法:下沉到数据库扣减库存:
- 并发校验库存,造成库存冲突的假象,扣成负数;
1.传入库存增量,库存会扣成负数
2.库存扣为负数
解决办法:
1.更新完库存之后,读取库存,若<0则抛出异常,回滚事务。
这样做没有加分布式锁,且数据库压力较大。且性能不高。
2.统一加锁,使之成为原子操作。
分布式锁
1.基于数据库的悲观锁
select ** for update 加锁,只能加一次,加锁期间其他线程不能获取。
具体实现:
优缺点:
简单方便,易于理解。并发量大的时候,对数据库压力大。锁的库与业务的数据库需要分开。
2.redis分布式锁:
SET resource_name my_random_value NX PX 3000
NX :因为redis是单线程的,并行请求变串行执行,只有一个能设置成功,设置成功则获得锁
PX: 失效时间,出现异常情况,锁可以过期失效。 这个时间怎么设置,程序执行超过这个时间了怎么办
释放锁:
使用redis的delete命令,正常释放的时候需要校验随机数是否相同,相同才能释放。
使用LUA脚本,去校验。
Redisson客户端
https://redisson.org/
还有springboot的配置方法等
https://github.com/redisson/redisson/tree/master/redisson-spring-boot-starter
redis官网有介绍:
防止释放其他线程的锁。
加锁实现:
释放锁,使用lua脚本,加在finally里:
3.zookeeper分布式锁
存储机构类似树,有持久节点和瞬时节点
瞬时节点有序,没有子节点,会话结束会消失。
zookeeper官网有介绍
zk观察器
- getData()
- getChildren()
- exists()
观察器只能观察一次,节点有变化发送给客户端
锁原理
- 十个线程创建是个节点,瞬时节点,序号最小的线程获得锁。
- 每个线程监听前一个序号节点;
- 线程执行完,删除自己的节点;
- 下一个序号线程收到通知,开始执行;
zk的Curator客户端
直接调用即可
官网: https://curator.apache.org/