首页 > 其他 > 详细

Redis分布式锁实现

时间:2020-02-09 14:45:19      阅读:68      评论:0      收藏:0      [点我收藏+]

Redis分布式锁实现

在分布式环境下,利用Redis实现锁机制,避免资源竞争的做法非常常见。这里探讨一下Redis分布式
锁的实现方式、可能存在的问题以及适用场景。

setnx

最常见的方法是利用Redis指令setnx,只有不存在的情况下才赋值,执行完毕释放资源,然后删除锁。

> setnx lock true
(integer) 1
... 执行业务操作...
> del lock
(integer) 1


这种方法的问题在于假如获取锁成功的客户端一直不释放,例如客户端挂掉的情况下,会导致死锁问题。
解决方法是增加锁超时时间,避免长时间不释放。示例命令如下,expire_ttl替换为默认的超时时间。

> setnx lock true
(integer) 1
> expire lock expire_ttl
(integer) 1
... 执行业务操作...
> del lock
(integer) 1


但是问题又来了,setnx和expire是两条指令,不是一个原子操作,万一运气不好,客户端setnx指令执行
成功后立刻挂掉,又出现原来的死锁问题怎么办?
解决办法是改为一条指令,利用Redis 2.6.12版本以后对set指令新增的扩展参数实现相同功能。示例命
令如下:

> set lock true nx ex expire_ttl
OK
... 执行业务操作...
> del lock
(integer) 1


设置了超时时间确实解决了死锁问题,但是万一客户端执行业务操作的时间超过了,会不会导致其他问题?
例如:
1、客户端A获得锁,执行业务操作
2、锁超时释放
3、客户端B获得锁,执行业务操作
4、客户端A完成业务,释放锁
5、客户端C也可以获得锁了
在以上场景中,由于超时机制,使得A错误释放了B获得的锁,导致C也错误得获得了锁。
解决方法是获取锁时设置唯一的数值,然后释放时校验锁的数值师范匹配,而不是统一设置相同的值。
数值可以生成一个随机数,为了确保随机数唯一的概率,可以使用时间戳+一些变量作为为随机数种子,
或者概率要求高的情况下可以考虑使用UUID。释放时校验也需要先获取值判断,匹配再删除,需要多
条指令,这种情况下使用lua脚本执行,保证任务的原子性。示例命令如下,random_num替换为具体
的随机值。示例命令如下:

> set lock random_num nx ex expire_ttl
OK
... 执行业务操作...
> eval "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock random_num
(integer) 1



这种做法可以避免客户端A错误释放客户端B设置的锁,但是还是无法避免客户端B获得锁。怎么解决呢?
一种是释放失败客户端A业务回滚记录日志,另外一种就是客户端A启用一个守护进程对超时时间的续期,
从而防止在业务未执行完成时错误获得锁。另外守护进场续期次数应该有一定限制,避免长时间占用资
源。示例命令如下,random_num替换为具体的随机值,min_ttl替换为守护进程进行续期时要求的存活
最小时间,expire_ttl替换为默认的超时时间。

> set lock random_num nx ex 5
OK
... 启动守护进程定时续期...
> eval "if redis.call('get', KEYS[1]) == ARGV[1] then if redis.call('ttl', KEYS[1]) < tonumber(ARGV[2]) 
then return redis.call('expire', KEYS[1], ARGV[3]) else return 1 end else return 0 end" 1 lock random_num min_ttl expire_ttl
(integer) 1
... 执行业务操作...
> eval "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock random_num
(integer) 1

Redlock算法

setnx只适用于单机的Redis或者分布式环境下对锁一致性要求不高的场合,对于多台Redis组成的集群
或者主从,可能会出现问题。例如,在某台Redis获取锁成功,随后这台Redis就挂掉,导致锁状态没有
同步到其他主机,又或者客户端分别在不同的主机同时获得了锁,诸如此类的问题影响了锁的作用。

具体实现:5个以上隔离部署在不同物理机上的Master节点,使用相同的key和随机值向所有节点同时获
取锁,流程图如下:
技术分享图片

Redis分布式锁实现

原文:https://www.cnblogs.com/yeyu456/p/12269908.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!