Redis集群环境下分布式锁方案-RedLock算法

2021/4/30 19:29:11

本文主要是介绍Redis集群环境下分布式锁方案-RedLock算法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

一、简介

二、Redis集群环境下分布式锁有什么问题?

三、RedLock 算法


一、简介

什么是分布式锁?

分布式锁就是保证某个时刻,只能有一个进程访问共享资源。比如在分布式环境下,要保证定时调度不能重复执行、执行扣减库存等操作不能同时有两个进程在执行,这些都可以使用分布式锁来解决。

一般分布式锁,通常要满足如下特性:

  • 1)、互斥性:同一时刻多个客户端对共享资源的访问存在互斥性;
  • 2)、防死锁:对锁设置超时时间,防止客户端一直占用着锁,即防止死锁;
  • 3)、可重入性:一个客户端上的同一个线程如果获取锁之后还可以再次获取这个锁(客户端封装,可以使用threadlocal存储持有锁的信息);
  • 4)、持锁人解锁: 客户端自己加的锁自己解除,不能将别人加的锁给删掉了;

实现分布式锁的方式有很多种,常见的有三种方式:

  • 1)、使用数据库方式,将执行的方法名称作为数据库主键,保证唯一;
  • 2)、Redis分布式锁,常见的使用Redisson开源框架;
  • 3)、Zookeeper分布式锁,利用ZK的临时有序节点 + watch监听机制实现;

Redis分布式锁官网地址:https://redis.io/topics/distlock,之前笔者也总结过Redis分布式锁的相关文章:

redis分布式锁学习总结
Zookeeper开源客户端curator 分布式锁

感兴趣的小伙伴可以回顾一下,当然,之前总结的Redis分布式锁都是单节点Redis环境下的,在生产环境中我们的Redis一般采用主从复制或者Redis Cluster方式部署,这样如果还是使用之前单节点的方式的话会不会存在什么问题呢?下面我们一起来看下。

二、Redis集群环境下分布式锁有什么问题?

我们知道,在Redis集群中,Master主节点和Slave从节点之间是异步方式进行数据同步的。这样其实就有一个问题,举个例子:

  • 1、客户端A在Master节点拿到了锁;
  • 2、这时候Master需要将锁的信息异步方式同步给Slave从节点,但是这个时候,还没同步完成【把客户端A创建的key写入Slave时】,Master节点挂了,锁没同步成功,这时候Redis会触发Master选举;
  • 3、此时Slave从节点升级成为新的Master主节点,注意此时新的master上面没有客户端A设置的key值,新master并不知道其实客户端A现在已经获取了锁;
  • 4、其他客户端,如客户端B判断到新Master上面没有那个key,直接set成功,客户端B也获取到和客户端A持有相同的key的锁;
  • 5、这样就违反了分布式锁互斥性了,同一个时刻,两个客户端持有相同的一把锁,想一下,要是这个执行逻辑是扣减库存操作,那么库存是不是可能导致超卖了;

以上就是Redis集群下Redis分布式锁存在的一些问题,当然如果项目里面容忍偶尔几次的这种锁重复问题,那么也可以采用,如果一次都不能容忍,那么建议使用即将要介绍的RedLock算法或者直接使用Zookeeper实现分布式锁。

三、RedLock 算法

为了解决Redis集群下Redis分布式锁存在的一些问题,Redis的作者提供了RedLock 算法来解决,能有效防止单点故障。

RedLock 算法的大体流程如下:假设有5个完全独立的redis主服务器

  • 1)、第一步:获取当前时间戳,单位是毫秒;
  • 2)、第二步:轮流用相同的key和value在5个redis节点上请求锁,客户端在每个master上请求锁时,会有一个和总的锁释放时间相比小的多的超时时间。比如如果锁自动释放时间是10秒钟,那每个节点锁请求的超时时间可能是5-50毫秒的范围,这个可以防止一个客户端在某个宕掉的master节点上阻塞过长时间,如果一个master节点不可用了,我们应该尽快尝试下一个master节点;
  •    比如:TTL为5s,设置获取锁最多用1s,所以如果一秒内无法获取锁,就放弃获取这个锁,从而尝试获取下个锁。
  • 3)、第三步:客户端通过获取所有节点获取的锁后的时间减去第一步的时间,这个时间差要小于TTL时间并且至少有3个redis实例成功获取锁,才能说明真正的获取锁成功;
  • 4)、第四步:如果成功获取锁,则锁的真正有效时间是 TTL减去第三步的时间差 的时间;比如:TTL 是5s,获取所有锁用了2s,则真正锁有效时间为3s,这里忽略时钟漂移的时间【实际上还要减去时钟漂移,我们假定没有时钟漂移,忽略不计】;
  • 5)、第五步:如果锁获取失败了,不管是因为获取成功的锁不超过一半(N/2+1),还是因为总消耗时间超过了锁释放时间【超过了TTL】,客户端都会到每个master节点上释放锁,即便是那些没有获取锁成功的节点上,也执行释放锁操作;

算法示意图如下:

上述就是RedLock算法的整体流程,能够在一定程序上解决Redis单点故障的问题,但是RedLock算法也有几个需要注意的地方:

【a】失败重试

当客户端获取锁失败后,应该在随机时间后重试获取锁,并设置一个最大重试次数;

并且最好在同一时刻并发的把set命令发送给所有redis实例,而且对于已经获取锁的client在完成任务后要及时释放锁;

【b】锁的真正有效时间计算方式是:TTL - (T2 - T1) - 时钟漂移

  • TTL指的是设置的锁的总超时时间;
  • T2指的是最后一次获取锁的时间戳;
  • T1指的是第一次获取锁的时间戳;

【c】多个客户端同时加锁的问题

举个例子,有三个客户端A,B,C, 客户端A在redis1、redis2上面加锁成功,客户端B在redis3、redis4加锁成功,客户端C在redis5加锁成功。想一下,这时候,没有一个客户端超过【N/2+ 1】半数以上,这时候所有客户端一直在尝试加锁,但是全部都尝试获取锁失败,这其实就产生了问题。所以建议多个客户端同时加锁的时候,可以再加上一点随机值,尽可能区分一下先后顺序;

【d】Redis节点崩溃恢复问题

同样举个例子,有三个客户端A、B,C,客户端A在redis1、redis2、redis3上加锁成功,说明客户端A获取锁成功,注意这时候如果锁的信息还没同步到redis4、redis5节点上的时候,redis3节点重启了,刚好客户端B在redis3、redis4、redis5中加锁成功,这时候客户端A和客户端B都拿到锁,违反了互斥性。

聪明的小伙伴肯定一下子想到,redis3节点开启持久化应该就可以了啊,开启了持久化机制确实可以,比如aof持久化方式有always实时同步、every second每秒同步策略:

  • always实时同步:导致性能急剧下降;
  • every second每秒同步:如果在一秒内断电,会导致数据丢失,立即重启会造成锁互斥性失效;

所以必须这种考虑,比较好的方式就是:

  • redis同步到磁盘方式保持默认的每秒,然后redis宕机之后要等待TTL时间后再重启,即等锁过期了再重启redis,保证不会出现同时两个客户端拿到锁,保证互斥性【延迟重启】;

同单节点Redis分布式锁一样,常见的RedLock算法也可以借助Redisson实现,感兴趣的小伙伴可以去搜哈一下。以上就是关于RedLock红锁算法的一些学习总结,希望对大家有所帮助。



这篇关于Redis集群环境下分布式锁方案-RedLock算法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程