Redlock实现库
Java Redisson Star 9458
C# RedLock.net Star 259
Go redsync.go Star 249
虽然后面的算法是一样的,不过这个点赞数确实服。
单点Redis锁
先简单回顾一下单点的Redis锁是怎么实现的。
获取锁
SET resource_name my_random_value NX PX 30000
客户端A使用Redis设置一个键值对,并指定超时时间以避免死锁。在其他客户端访问时,它们首先会检查该键是否已存在,且其值是否为“my_random_value”。如果已存在就等待,否则就获取成功,执行业务代码。所有客户端共享且已知的对象包括resource_name和my_random_value。
释放锁
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1])else return 0end
对比key获取到的对应的value是否相等,如果相等,就删除(释放),否则就返回失败。
单点Redis锁的缺陷
只有一个Redis实例时,出现故障会导致所有依赖它的服务都崩溃,这个缺陷异常明显。显然不太适合大型的应用。
简单的Redis主从架构碰到的问题
为了避免单点故障,我们给Redis做一个Master/Slave的主从架构,一个Master,一台Slave。下面就会碰到这么一个问题。下面是使用场景。
客户端A在Master上获取到一个锁。
Master把这个数据同步到Slave的时候挂了(因为Master和Slave之间同步是异步的)。
Slave变成了Master。
客户端B通过相同的key,和value获取到锁。分布式锁失效
Redlock算法
假设我们有N(假设5)个Redis master实例,所有节点相互独立,并且业务系统也是单纯的调用,并没有什么其他的类似消息重发之类的辅助系统。下面来模拟一下算法:
1.客户端获取服务器当前的的时间t0,毫秒数。
在5个实例中使用相同的key和value获取锁。在获取业务锁时,客户端会设置一个持续时间远小于业务锁需要的超时时间。举个例子,假设锁需要10秒钟,可以将超时时间设置在5-50毫秒之间。重写后的句子: 为了避免客户端在尝试获取锁时出现Redis已经故障的情况,需要采取措施。超时了之后就直接跳到下一个节点。
3.客户端通过当前时间(t1)减去t0,计算获取锁所消耗的时间t2(=t1-t0)。只有t2小于锁的业务有效时间(也就是第二步的10秒),并且,客户端在至少3(5/2+1)台上获取到锁我们才认为锁获取成功。
4.如果锁已经获取,那么锁的业务有效时间为10s-t2。
5.如果客户端没有获取到锁,可能是没有在大于等于N/2+1个实例上获取锁,也可能是有效时间(10s-t2)为负数,我们就尝试去释放锁,即使是并没有在那个节点上获取到。
锁的释放
释放比较简单,直接删除所有实例上对应的key就好。
以上是Redis中分布式锁Redlock的示例分析的详细内容。更多信息请关注PHP中文网其他相关文章!