先聊下redis普通的分布式锁,用
1.单节点、主从/哨兵模式的分布式锁,安全吗?
或许你了解过,通过如下方式加锁:
设置锁时,使用set命令,因为其包含了setnx,expire的功能,起到了原子操作的效果,给key设置随机值,并且只有在key不存在时才设置成功返回True,并且设置key的过期时间(最好用毫秒)
1 SET key_name my_random_value NX PX 30000 # NX 表示if not exist 就设置并返回True,否则不设置并返回False PX 表示过期时间用毫秒级, 30000 表示这些毫秒时间后此key过期
2.在获取锁后,并完成相关业务后,需要删除自己设置的锁(必须是只能删除自己设置的锁,不能删除他人设置的锁);
删除原因:保证服务器资源的高利用效率,不用等到锁自动过期才删除;
删除方法:最好使用Lua脚本删除(redis保证执行此脚本时不执行其他操作,保证操作的原子性),代码如下;逻辑是 先获取key,如果存在并且值是自己设置的就删除此key;否则就跳过;
1 if redis.call("get",KEYS[1]) == ARGV[1] then 2 return redis.call("del",KEYS[1]) 3 else 4 return 0 5 end
单点Redis锁的缺陷:这个缺陷其实很明显,如果只有一个Redis实例,这个挂了,所有依赖他的服务都挂了。显然不太适合大型的应用。
2.升级为主从架构模式的问题
为了避免单点故障,我们给Redis做一个Master/Slave的主从架构,一个Master,一台Slave。下面就会碰到这么一个问题。下面是使用场景。
- 客户端A在Master上获取到一个锁。
- Master把这个数据同步到Slave的时候挂了(因为Master和Slave之间同步是异步的)。
- Slave变成了Master。
- 客户端B通过相同的key,和value获取到锁。分布式锁失效
- 释放锁的操作,得释放自己加的锁。
3.redisRedlock
全名叫做 Redis Distributed Lock;即使用redis实现的分布式锁; RedLock官网说明 Distributed locks with Redis,英文不好的小伙伴,可以参考这篇文章,坐着翻译的还不错 《Redis分布式锁RedLock》
个人总结如下:
Redlock算法
假设我们有N(假设5)个Redis master实例,所有节点相互独立,并且业务系统也是单纯的调用,并没有什么其他的类似消息重发之类的辅助系统。下面来模拟一下算法:
- 客户端获取服务器当前的的时间t0,毫秒数。
- 使用相同的key和value依次向5个实例获取锁。客户端在获取锁的时候自身设置一个远小于业务锁需要的持续时间的超时时间。举个例子,假设锁需要10秒,超时时间可以设置成比如5-50毫秒。这个避免某个Redis本身已经挂了,但是客户端一直在尝试获取锁的情况。超时了之后就直接跳到下一个节点。
- 客户端通过当前时间(t1)减去t0,计算获取锁所消耗的时间t2(=t1-t0)。只有t2小于锁的业务有效时间(也就是第二步的10秒),并且,客户端在至少3(5/2+1)台上获取到锁我们才认为锁获取成功。
- 如果锁已经获取,那么锁的业务有效时间为10s-t2。
- 如果客户端没有获取到锁,可能是没有在大于等于N/2+1个实例上获取锁,也可能是有效时间(10s-t2)为负数,我们就尝试去释放锁,即使是并没有在那个节点上获取到。
锁的释放
释放比较简单,直接删除所有实例上对应的key就好。确定要释放的是自己所的value.别释放了他人的。
以上是RedLock释放锁的源码,用到了
Future模式、Lua脚本,这部分可以自己了解下,看源码总是头疼,但是大牛写的源码就是这样。以后会专门写一篇分析redLock源码的文章。