假设有5个彻底独立的redis主服务器redis
获取当前时间戳算法
client尝试按照顺序使用相同的key,value获取全部redis服务的锁,在获取锁的过程当中的获取时间比锁过时时间短不少,这是为了避免要过长时间等待已经关闭的redis服务。而且试着获取下一个redis实例。 好比:TTL为5s,设置获取锁最多用1s,因此若是一秒内没法获取锁,就放弃获取这个锁,从而尝试获取下个锁安全
client经过获取全部能获取的锁后的时间减去第一步的时间,这个时间差要小于TTL时间而且至少有3个redis实例成功获取锁,才算真正的获取锁成功服务器
若是成功获取锁,则锁的真正有效时间是 TTL减去第三步的时间差 的时间;好比:TTL 是5s,获取全部锁用了2s,则真正锁有效时间为3s(其实应该再减去时钟漂移);网络
若是客户端因为某些缘由获取锁失败,便会开始解锁全部redis实例;由于可能已经获取了小于3个锁,必须释放,不然影响其余client获取锁并发
能够当作是同步算法;由于 即便进程间(多个电脑间)没有同步时钟,可是每一个进程时间流速大体相同;而且时钟漂移相对于TTL叫小,能够忽略,因此能够当作同步算法异步
当client不能获取锁时,应该在随机时间后重试获取锁;而且最好在同一时刻并发的把set命令发送给全部redis实例;并且对于已经获取锁的client在完成任务后要及时释放锁,这是为了节省时间;分布式
因为释放锁时会判断这个锁的value是否是本身设置的,若是是才删除;因此在释放锁时很是简单,只要向全部实例都发出释放锁的命令,不用考虑可否成功释放锁;post
先假设client获取全部实例,全部实例包含相同的key和过时时间(TTL) ,但每一个实例set命令时间不一样致使不能同时过时,第一个set命令以前是T1,最后一个set命令后为T2,则此client有效获取锁的最小时间为TTL-(T2-T1)-时钟漂移;性能
对于以N/2+ 1(也就是一半以 上)的方式判断获取锁成功,是由于若是小于一半判断为成功的话,有可能出现多个client都成功获取锁的状况, 从而使锁失效
一个client锁定大多数事例耗费的时间大于或接近锁的过时时间,就认为锁无效,而且解锁这个redis实例(不执行业务) ;只要在TTL时间内成功获取一半以上的锁即是有效锁;不然无效
可以自动释放锁
在获取锁失败(不到一半以上),或任务完成后 可以自动释放锁,不用等到其自动过时
在client重试获取哦锁前(第一次失败到第二次重试时间间隔)大于第一次获取锁消耗的时间;
重试获取锁要有必定次数限制
若是Client进行的工做耗时较短,那么能够默认使用一个较小的锁有效期,而后实现一个锁续约机制。
当一个Client在工做计算到一半时发现锁的剩余有效期不足。能够向Redis实例发送续约锁的Lua脚本。若是Client在必定的期限内(耗间与申请锁的耗时接近)成功的续约了半数以上的实例,那么续约锁成功。
为了提升系统的可用性,每一个Client申请锁续约的次数须要有一个最大限制,避免其不断续约形成该key长时间不可用。
若是redis没有持久化功能,在clientA获取锁成功后,全部redis重启,clientB可以再次获取到锁,这样违法了锁的排他互斥性;
若是启动AOF永久化存储,事情会好些, 举例:当咱们重启redis后,因为redis过时机制是按照unix时间戳走的,因此在重启后,而后会按照规定的时间过时,不影响业务;可是因为AOF同步到磁盘的方式默认是每秒-次,若是在一秒内断电,会致使数据丢失,当即重启会形成锁互斥性失效;但若是同步磁盘方式使用Always(每个写命令都同步到硬盘)形成性能急剧降低;因此在锁彻底有效性和性能方面要有所取舍;
有效解决既保证锁彻底有效性及性能高效及即便断电状况的方法是redis同步到磁盘方式保持默认的每秒,在redis不管由于什么缘由停掉后要等待TTL时间后再重启(学名:延迟重启) ;缺点是 在TTL时间内服务至关于暂停状态;
1.TTL时长 要大于正常业务执行的时间+获取全部redis服务消耗时间+时钟漂移
2.获取redis全部服务消耗时间要 远小于TTL时间,而且获取成功的锁个数要 在总数的通常以上:N/2+1
3.尝试获取每一个redis实例锁时的时间要 远小于TTL时间
4.尝试获取全部锁失败后 从新尝试必定要有必定次数限制
5.在redis崩溃后(不管一个仍是全部),要延迟TTL时间重启redis
6.在实现多redis节点时要结合单节点分布式锁算法 共同实现
假设有5个redis实例a,b,c,d,e,线程A分别加锁,而后a,b,c加锁成功,由于网络分区问题,d,e失败。同时,线程B也加锁,由于网络分区问题,a,b加锁失败,可是d,e加锁成功。这时候,显然线程A会得到锁,可是若是这时候c的master挂了,而后切换成了slave,slave中没有A加锁的信息,恰巧这时线程B对c加锁的命令到了,那线程B就会加锁成功,这时候,线程A和B都给三个节点加锁成功了,他们就同时拥有锁。因此仍是不是彻底可靠的!!!
推荐你们阅读这个Redisson官方文档 - Redisson项目介绍