分布式锁相关


超卖问题的解决

现象

  • 一件商品,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/