分布式锁的两种实现原理

分布式锁:用为了解决分布式服务场景下,多个服务并发操作数据导致并发安全问题

 

Redis分布式锁实现原理

setnx实现方式:
set一个key为 订单id等锁唯一标识,value为一串随机数uuid等,如果key存在则失败返回0,成功返回1,设置超时时间后自动删除(防止死锁)
尝试去获取锁就是尝试去set(key,value),如果这个key已经存在了 就set失败 获取锁失败,否则获取锁成功
获取锁失败就触发重试机制 直到上锁成功.
 

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),

 

posted @ 2020-09-15 18:04  六小扛把子  阅读(442)  评论(0编辑  收藏  举报