1,单机实现分布式锁的脚本(官方推荐实现)html
SET lock_key random_value NX PX 10000
// do sth
eval "if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end"
复制代码
2,注意事项(对释放锁的控制,以及锁超时的控制)random_value 要保证惟一,能够用 trace_id 来保证!
3,存在的问题,单机Redis只是依赖单台 Redis ,当依赖的 Redis 挂掉以后会形成比较大的问题!
4,那么部署 Redis 的主从能够保证吗?主要缘由是 Redis 主节点与从节点之间的数据同步是异步的。redis
Redlock 算法是基于 N 个彻底独立的 Redis 节点(一般状况下 N 能够设置成 5)。
1,获取当前时间(毫秒数)。算法
2,按顺序依次向 N 个 Redis 节点执行获取锁的操做。这个获取操做跟基于单 Redis 节点的获取锁的过程相同。为了保证在某个 Redis 节点不可用的时候算法可以继续运行,这个获取锁的操做还有一个超时时间(time out),它要远小于锁的有效时间(几十毫秒量级)。客户端在向某个 Redis 节点获取锁失败(好比该Redis节点不可用,或者该 Redis 节点上的锁已经被其它客户端持有)之后,应该当即尝试下一个 Redis 节点。数据库
3,计算整个获取锁的过程总共消耗了多长时间,计算方法是用当前时间减去第1步记录的时间。若是客户端从大多数Redis节点(>= N/2+1)成功获取到了锁,而且获取锁总共消耗的时间没有超过锁的有效时间(lock validity time),那么这时客户端才认为最终获取锁成功;不然,认为最终获取锁失败。安全
4,若是最终获取锁成功了,那么这个锁的有效时间应该从新计算,它等于最初的锁的有效时间减去第3步计算出来的获取锁消耗的时间。bash
5,若是最终获取锁失败了(可能因为获取到锁的 Redis 节点个数少于 N/2+1 ,或者整个获取锁的过程消耗的时间超过了锁的最初有效时间),那么客户端应该当即向全部 Redis 节点发起释放锁的操做(这里来保证全部的 Redis 节点均可以报以获取的锁释放掉)。服务器
Redlock 算法实现的前提是基于不一样的机器具备相同的时钟,或者偏差很小能够忽略不计的假设。(这也是DDIA做者喷的一点)网络
存在的问题: 1,关于Redis的持久化的问题 假设一共有5个Redis节点:A, B, C, D, E。设想发生了以下的事件序列:
1.1 客户端1成功锁住了A, B, C,获取锁成功(但D和E没有锁住)。
1.2 节点C崩溃重启了,但客户端1在C上加的锁没有持久化下来,丢失了。
1.3 节点C重启后,客户端2锁住了C, D, E,获取锁成功。
dom
Redis 给出的解决方案:延迟重启,既当 Redis 节点挂掉以后不要马上重启,而要等待一个锁的过时时间以后再重启。异步
2,若是获取锁消耗的时间过多以致于没法完成后续的操做,如何释放锁? 我的认为须要业务方本身拿捏一个业务操做的须要消耗的时长,
3,Redis 做者在设计Redlock的时候,是充分考虑了网络延迟和程序停顿所带来的影响的。可是,对于客户端和资源服务器之间的延迟(即发生在算法第3步以后的延迟),他认可全部的分布式锁的实现,包括 Redlock,是没有什么好办法来应对的。
在 Martin 的这篇文章中,他把锁的用途分为两种:
1,带有自动过时功能的分布式锁,必须提供某种fencing机制来保证对共享资源的真正的互斥保护。Redlock 提供不了这样一种机制。
2,Redlock 构建在一个不够安全的系统模型之上。它对于系统的记时假设(timing assumption)有比较强的要求,而这些要求在现实的系统中是没法保证的。 Redlock 算法对机器时钟的强依赖,Martin 认为算法的实现不该该对时序作任何假设:进程可能会暂停任意时长,数据包可能会在网络中被任意延迟,时钟可能会被任意错误,即使如此,该算法仍能够正确执行并给出正确结果。
关于时钟的不可靠性:Redis 做者认为 Redlock 对时钟的要求,并不须要彻底精确,它只须要时钟差很少精确就能够了。
3,在 Redlock 第三步完成以后的网络延迟,也为形成 Redlock 算法的失效,可是这个问题并非 Redlock 算法独有的
Martin得出了以下的结论:
在了解了 Redlock 算法的逻辑及其可能存在的问题以后,咱们能够针对本身的业务场景进行选择~