先说说redis分布式锁的原理。
加锁:setnx一个key,返回1说明加锁成功,0说明锁已经存在。
解锁:del该key。

原理说完了,再讨论特殊情况。
1.在set之后,如果客户端挂了,锁会一直存在,导致死锁。
那有人会说,用expire。
但是两个命令无法保证原子性。
还好,redis从2.6.12开始,set命令支持一系列参数来修改。

set key value EX seconds NX 

2.对单机来说没问题,但是在集群环境下还是有问题。redis集群数据同步是异步的,当master节点获得锁后崩溃了,数据还没同步到其他master节点,那么新的master还是有可能获得锁。
如何解决单节点故障问题,redis提供了redlock算法。

3.加锁讨论完,再讨论解锁。解锁要保证删除的是自己设置的锁。正常逻辑是先get,判断锁是否存在,存在则删除。但是有个问题,如果程序在加锁后执行时间很长且锁过期时间较短,get到锁后锁自动过期了,并且别的客户端加锁成功,若此时删除就删了别人的锁。
所以,在get和del之间不能有其他命令,redis做不到,可以通过lua实现

'if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end'

因为lua执行可以保证原子性。
lua执行多条命令,由于redis是单线程,中间不会有穿插其他命令。
如果不用lua,执行多条命令可能中间有别得命令执行。

集群模式

上面说的是单机模式下的,如果是集群模式下,刚获得锁主节点就挂了,锁还没有同步到从节点,此时从节点切换为主节点,此时已经没有锁,任何连接都可以设置锁了,但问题是之前的连接还未执行释放锁,若此时执行释放锁操作则会释放别人的锁,这就会出现问题。丧失锁的安全性。针对这个问题,成熟的方案是redlock算法。

redlock算法

标签: 分布式锁, redis

添加新评论