REDIS 学习(10)流程图解使用redis实现分布式锁

23 篇文章 1 订阅
16 篇文章 0 订阅

redis作为集中式缓存,可以通过它来实现分布式锁。

首先用到的redis操作有:

setnx key value:      当key不存在的时候生效并返回1,当已经有此key的时候返回0

getset key value:     设置新值返回旧值,如果之前不存在也设置新值并返回nil

get key:     返回对应的值,没有则返回nil

del key,key1,key2:      删除,返回删除成功的个数

方案1,使用setnx key value来获得锁,del来释放锁

                                           图1

方案1有个问题是,如果获得锁的线程或者进程崩溃了,这个锁将得不到释放

方案2我们增加将值设置为当前时间,引入超时判断

图2

方案2依然有个问题,并发情况下,同时判断锁超时,n0,n1,n2同时del并通过setnx获取锁可能,会n2把n1获取的锁给删除掉。

方案3增加getset 获取旧的时间设置新的时间,这样删除某个超时锁的操作只有一起,其他的获取锁判断会接下来判断锁没有超时。

图3

注意虚线部分,这是之前其他某个博主的做法,就是谁设置了有效时间戳后就认为获取了这个锁。而实线部分我的做法是获取超时锁后就释放掉,然后重新获取锁。两者应该区别不是太大,都是通过getset解决超时锁只进行一次删除的问题。

 

最后,依然会有个悬而未决的问题,最初获取这个锁的线程或进程,如果在超时后,锁被其他线程或者进程重新获取后,这个线程或者进程如果复活了,也会再次触发释放锁(del)?

所以我认为,del操作应该增加类似版本号的判断,本文例子用它的时间戳即可

 

2017年2月7日更正:

由于setnx不能设置过期时间,所以通过setnx结合expire的方式在图1中就能实现锁的过期释放策略:

if(setnx(a,'tag') =ok){

   expire a 1;

}

不过set a  tag ex 1 nx就已经实现了setnx+expire这两步操作,设值返回ok,已经存在返回null。由于是一个redis命令所以是原子操作。而且未来的redis版本可能去掉setnx和setex, 因此推荐用set ex nx的方式。当然为了防止假死的线程复活后删除锁,在set 的时候依然可以通过增加本版本号或者使用随机数的方式来处理。

2019年10月13日补充:

总结了设置锁的三个要素,set nx ex 保证没有值才能设置成功1️⃣,过期时间2️⃣, 其中key是锁的名字值要带上版本号3️⃣。

但是删除的过程 怎么保证自己的线程能够删除自己的版本号的锁?

思路:还得是CAS

方案一  redis提供脚本做这件事,删除值的时候可以比较expect value一致才删除

方案2 使用redis 的事务 watch + multi + exec来实现CAS乐观锁修改,watch后,mulit到exe之间对此键的值修改,如果值期间被别人改过会提交失败

127.0.0.1:6379> get a

"102"

127.0.0.1:6379> watch a

OK

127.0.0.1:6379> get a

"102"

------------->>这里别的redis客户端修改了值>>------

127.0.0.1:6379> multi 

OK

127.0.0.1:6379> set a 1

QUEUED

127.0.0.1:6379> exec

(nil)

127.0.0.1:6379> get a

"103"

127.0.0.1:6379> 

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值