此文是对http://zhangtielei.com/posts/blog-redlock-reasoning-part2.html文章的个人归纳,如有问题请联系删除
什么是redlock
redlock是redis给出的分布式锁的实现规范
先说说基于单节点redis实现的分布式锁
SET resource_name random_value NX PX 30000 如果执行成功说明获取到锁了
random_value需要设置一个唯一标识避免冲突
resource_name 只有不存在的时候才能set成功 代表获取到了锁
px 30000 代表过期时间30秒
需要注意的是
1. 过期时间必须得设置 避免锁不能释放的情况
2. random_value还是很有必要的必须不重复 确保锁只能被锁的持有者删除
举例:
A获取锁 执行超过px时间 锁被释放
B获取锁成功 执行
A执行完释放锁 这时B是没有锁保护的
3. 释放锁必须要用lua脚本保证原子性
4. 在主从的情况下,A在master获取到锁 如果master宕机 并且 key还未同步到slave B从slave也可以获取到锁
由于第4点 单节点redis无法解决这个问题,所以才有了redlock
redlock算法获取锁的步骤
1. 记录当前时间
2. 按顺序依次向客户端申请锁,跟单节点申请锁一样只是多了一个超时时间用于redis节点不可用的情况(几十毫秒级别),获取锁失败立即向下一个客户端申请锁
3. 当前时间减去第一步获取的时间,如果n/2+1的节点获取锁成功,并且消耗时间未超过锁的有效时间,获取锁成功否则失败
4.重新计算锁的有效时间,有效时间减去第三步算出来的时间
5. 如果失败则向所有的redis节点发送lua删除锁操作 (避免redis加锁成功但是客户端未受到消息的情况)
虽然避免了failover但是 节点崩溃重启的时候仍然有安全性问题
1. A客户端锁住了 123节点 45没锁住
2. 此时3节点崩溃但是A客户端在上面加的锁未持久化
3. 3节点重启 B客户端锁住了 345 获取锁成功
解决方法有:
1. 延迟重启,节点崩溃了不立即重启,而是等到超过锁的失效时间之后再重启
依赖系统时钟导致的不安全性
1. A客户端在123节点获取锁成功
2.3节点时钟发生向前跳跃只是3节点上的锁过期
3.B客户端获取了345节点的锁
gc pause导致的不安全性
1. A客户端在接收到12345节点的成功获取锁之前 进行了gc pause超过了锁的有效时间
2. B客户端获取了123节点的锁
3. A节点恢复过来之后 拿到了成功的结果 认为自己获取锁成功了
注意:这个是在获取到成功信息之前 锁就过期了,以前是客户端获取到成功信息之后 锁才过期,破坏了锁服务本身的安全性 这个是不成立的对于redlock 因为redlock有一步是检查获取锁的时间是否超过锁的有效时间
zookeeper实现分布式锁
zookeeper的比redis功能多的地方是
创建/node之后 如果客户端长时间不能应答心跳 zookeeper会删除这个失联客户端创建的所有node节点 不用想redis一样考虑如何设置锁的过期时间
如果锁被A客户端持有,B客户端来申请锁失败,这时B客户端可以watch这个/node 当node锁被释放的时候 通知客户端B去获取锁 实现阻塞等待的功能