分布式锁的两种实现原理
分布式锁:用为了解决分布式服务场景下,多个服务并发操作数据导致并发安全问题
Redis分布式锁实现原理
redis setnx形式实现分布式锁会遇到的几个问题:
1. 如何防止死锁?
设置过期时间,超时自动删key
2. set if not exist 与 set expire time 是两条语句,如何保证原子性?
redis客户端某个版本之后(好像是2.8吧) 原生支持这两条原子操作的指令,或者直接用Lua脚本指令保证原子性
3. 服务A持有锁过期了,服务B占有了锁, 服务A执行完删掉了别人的锁,这种情况如何控制?
删key之前先判断持有锁的服务是不是当前服务了,那如何保证这次判断和删除两步是原子操作呢? lua脚本安排
基于redission框架实现分布式锁
原生支持redis集群的分布式锁 (key根据hash slot路由到对应的master节点)
执行lua脚本加锁 (set key + 过期时间)
加锁成功出发 watch dog 监视器,在锁未释放之前,每隔一段时间去判断一下当前持有锁的服务是不是自己,如果是自己就刷新一下过期时间,目的就是防止没执行完他就自己过期了嘛
Zookeeper分布式锁实现原理
利用Zookeeper的顺序临时节点来完成分布式锁
临时节点 是为了防止死锁 当持有锁的节点挂掉,会直接删除掉临时节点 释放锁来给其他节点
顺序节点 是为了防止当持久锁的节点释放锁 造成羊群效应,所有节点来抢锁造成并发压力
先创建父节点为锁节点
当客户端想占有所就在父节点下创建顺序节点,
每个顺序节点判断自己是否是最靠前节点,如果是就获取了锁,如果不是就监听上一个节点
Redis与Zookeeper做分布式锁如何选型
分布式服务加锁更推荐于zookeeper,zookeeper原生就是为了分布式协调用的,redis更倾向于用作缓存
从高并发度看,redis做分布式锁比较合适,redis主从架构加集群 能抗更高并发量,
zookeeper一般只配置个三五台用作分布式协调,不适用于高并发争抢锁场景
Redis高并发场景加锁优化
例如每秒10000个请求请求同一商品库存,如何加锁优化?
1. 一般这种高并发场景扣减库存场景 可以不加锁 直接用redis扣减,先不操作数据库。利用redis单线程处理特点,每一个请求执行 先查库存 再减库存两条命令合成lua脚本,保证原子操作
直接在redis中扣减库存 扣减完判断是否库存负数,负数回滚 提示扣减失败 防止超卖,然后扣减动作发到MQ异步落到数据库
2. 用分段锁+合并加锁
数据库中把库存的字段设置成十个字段 (库存01,库存02,库存03...库存10)
如果总共10000个库存,那每个字段保存1000个库存,并发改库存分别对不同的库存字段加锁,提高10倍并发度
根据不同服务的请求 映射到不同字段,也可以通过随机数 随机分发到不同字段(搞一个map key存1-10 value存库存01 - 库存10 当库存减完直接删掉key),