Redis应用篇(众星追月):分布式锁

Redis应用篇(众星追月):分布式锁

作者: 小龙coding 2021-10-26 19:37:15

存储

存储软件

分布式

Redis 由于分布式系统「多线程」、「多进程」并且「分布在不同机器」上,这将使原单机并发控制锁策略失效,为了解决这个问题就需要一种「跨JVM的互斥机制」来控制共享资源的访问,这就得靠分布式锁啦。

[[431326]]

话题引入

大家好,我是小龙。

之前在《吃透Redis系列》专栏发表了第一篇文章《Redis基础篇(万丈高楼平地起):核心底层数据结构》简单介绍了Redis,以及它的内部组织形式、核心数据结构与大致使用场景。还没看到得同学可以回过头看看。

接下来,我将继续带大家深入理解,本文将介绍Redis高频使用的一个场景——「利用Redis实习分布式锁」。

想必大家都知道,在遇到并发问题时,我们通常会使用锁来解决并发问题。

这是,有同学可能说:“这个我会,不就用synchronized、Lock这些实现吗?”

对,你说的不错。但是你只说对了一半,在「传统单机部署」的情况下,可以使用Java并发处理相关的API(如ReentrantLcok或synchronized)进行互斥控制。

但是在「分布式系统」中,由于分布式系统「多线程」、「多进程」并且「分布在不同机器」上,这将使原单机并发控制锁策略失效,为了解决这个问题就需要一种「跨JVM的互斥机制」来控制共享资源的访问,这就得靠分布式锁啦。

看透锁本质

在我看来:所有的锁本身都可以用一个变量来表示。

比如:在「单机上运行」的多线程程序来说。取一个变量,变量为0时,表示没有线程获取锁;变量为1时,表示已经有线程获取锁。

加锁:线程调用加锁操作,检查变量是否为0,如果为0,表示没线程获取锁,将变量设置为1,表示获取锁;如果不是0,表示其他线程已经暂用锁,获取锁失败。

解锁:同理。

而分布式环境下,同样可以以变量形式理解分布式锁。

但是,和线程在单机上操作锁不同的是,在分布式场景下,「锁变量需要由一个共享存储系统来维护」,只有这样,多个客户端才可以通过访问共享存储系统来访问锁变量。相应的,「加锁和释放锁的操作就变成了读取、判断和设置共享存储系统中的锁变量值」。

「可见,满足分布式锁的要求」:

  • 「锁操作原子性」:分布式锁的加锁和释放锁的过程,涉及多个操作。所以,在实现分布式锁时,我们需要保证这些「锁操作的原子性」;
  • 「锁的可靠性」:共享存储系统保存了锁变量,如果共享存储系统发生故障或宕机,那么客户端也就无法进行锁操作了。在实现分布式锁时,我们需要考虑保证「共享存储系统的可靠性」,进而保证「锁的可靠性」。

上面我们提到了可以使用一个锁变量来表示锁,其实你也可以理解为「占位」。只不过分布式锁需要把这个坑位拿出来放于「共享」的地方,每个都从「共享处来检查坑位」。

占位一般是使用 setnx(set if not exists) 指令,只允许被一个客户端占位。先来先占, 用完了,再调用 del 指令释放茅坑。

  1. //加锁 
  2. > setnx lock_key 1 
  3. OK 
  4. //业务逻辑 
  5. >(其他操作) 
  6. //释放锁 
  7. > del lock_key 

但是有个问题,如果逻辑执行到中间出现异常了,可能会导致 del 指令没有被调用,这样就会「陷入死锁」,锁永远得不到释放。

于是我们在拿到锁之后,再给锁加上一个过期时间,这样即使中间出现异常也可以保证指定时间之后锁会自动释放。

  1. //加锁 
  2. > setnx lock_key 1 
  3. OK 
  4. > expire lock_key 5 
  5. //业务逻辑 
  6. >(其他操作) 
  7. //释放锁 
  8. > del lock_key 

但是以上逻辑还有问题。如果在 setnx 和 expire 之间服务器进程突然挂掉了,可能是因为机器掉电或者是被人为杀掉的,就会导致 expire 得不到执行,也会造成死锁。

这种问题的根源就在于 setnx 和 expire 是两条指令而不是原子指令。你也许会想到使用事务什么的执行,但是这里不行,因为如果 setnx 没抢到锁,expire 是不应该执行的。

Redis 2.8 版本中作者加入了 set 指令的扩展参数,使得 setnx 和expire 指令可以一起执行,彻底解决了分布式锁的乱象。

  1. set key value [EX seconds | PX milliseconds] [NX] 

除了上述基本常规的问题,还有这些「你可能没考虑到的问题」:

超时问题

Redis 的分布式锁不能解决超时问题,如果在加锁和释放锁间的业务逻辑执行时间太长,以至于超出了锁的超时限制,就会出现问题(也就是锁过期了,你的业务逻辑还没执行完)。

因为这时候锁过期了,第二个客户端B重新持有了这把锁,但是紧接着客户端A执行完了业务逻辑,就把锁给释放了,客户端C就会在客户端B逻辑执行完之间拿到了锁。为了避免这个问题,Redis 分布式锁不要用于较长时间的任务。

为了应对这个问题,我们需要能区分来自不同客户端的锁操作,具体咋做呢 ? 针对于这个问题,我们可以想办法把命令略加点小技巧。可以在锁变量的值上想想办法。

在使用SETNX命令进行加锁的方法中,我们通过把锁变量值设置为1或0,表示是否加锁成功。1和0只有两种状态,无法表示究竟是哪个客户端进行的锁操作。

所以,我们在加锁操作时,可以「让每个客户端给锁变量设置一个唯一值」,这里的唯一值就可以用来标识当前操作的客户端。

在释放锁操作时,客户端需要判断,当前「锁变量的值是否和自己的唯一标识相等」,只有在相等的情况下,才能释放锁。这样一来,就不会出现误释放锁的问题了。

于是,我们的命令可以这样写:

  1. //加锁,unique_value作为客户端唯—性的标识 
  2. SET lock_key unique_value NX PX 5000 

其中,unique_value 是客户端的唯一标识,可以用一个随机生成的字符串来表示,PX 5000则表示 lock_key会在5s后过期,以免客户端在这期间发生异常而无法释放锁。

因为在加锁操作中,每个客户端都使用了一个唯一标识,所以在「释放锁操作」时,我们需要「判断锁变量的值」,是否等于执行释放锁操作的客户端的唯一标识,如下所示,可以使用Lua脚本来保证原子性:

  1. //释放锁比较unique_value是否相等,避免误释放 
  2. if redis.call("get" ,KEYS[1])== ARGV[1] then 
  3.  return redis.call("del" , KEYS[1]) 
  4. else 
  5.  return 0 
  6. end 

可重入性

可重入性是指线程在持有锁的情况下再次请求加锁,如果一个锁支持同一个线程的多次加锁,那么这个锁就是可重入的。比如 Java 语言里有个 ReentrantLock 就是可重入锁。

Redis 分布式锁如果要支持可重入,可以对客户端的 set 方法进行包装,使用线程的 Threadlocal 变量存储当前持有锁的计数。

此处就不过多介绍,大抵不会问,有兴趣可以自己上网查阅看书。

课外补充

上述内容,是个基于单个Redis节点实现分布式锁。

当我们要实现「高可靠的分布式锁」时,就不能只依赖单个的命令操作了,我们需要按照一定的步骤和规则进行加解锁操作,否则,就可能会出现锁无法工作的情况。“一定的步骤和规则”是指啥呢?其实就是分布式锁的算法。

这里简单介绍Redlock算法的执行步骤。Redlock算法的实现需要有N个独立的Redis实例。接下来,我们可以分成3步来完成加锁操作。

1、客户端获取当前时间

2、客户端按照顺序在每个Master实例中尝试获得锁。在获得锁的过程中,为每一个锁操作设置一个快速失败时间(如果想要获得一个10秒的锁,那么每一个锁操作的失败时间设为5-50ms)。

这样可以避免客户端与一个已经故障的Master通信占用太长时间,通过快速失败的方式尽快的与集群中的其他节点完成锁操作。

3、客户端计算出与master获得锁操作过程中消耗的时间,「当且仅当Client获得锁消耗的时间小于锁的存活时间,并且在一半以上的master节点中获得锁」。才认为client成功的获得了锁。

4、如果已经获得了锁,「Client执行任务的时间窗口是锁的存活时间减去获得锁消耗的时间。」

 

5、如果Client获得锁的数量不足一半以上,或获得锁的时间超时,那么认为获得锁失败。客户端「需要尝试在所有的master节点中释放锁, 即使在第二步中没有成功获得该Master节点中的锁,仍要进行释放操作。」

 

文章来源网络,作者:管理,如若转载,请注明出处:https://shuyeidc.com/wp/244731.html<

(0)
管理的头像管理
上一篇2025-04-25 20:56
下一篇 2025-04-25 20:57

相关推荐

  • 站群服务器和普通服务器到底哪个更适合GEO,怎么选?

    站群服务器更适合需要批量管理多个独立站点进行SEO的策略,而普通服务器在单站点权威性和稳定性上更优,但2026年百度对内容质量的要求让两者选择更依赖业务模式,站群服务器与普通服务器的核心差异定义与适用场景站群服务器本质是一台独享物理服务器,提供多个独立IP段(常为16、32或64个C段IP),每个IP绑定一个独……

    2026-07-28
    0
  • 物理服务器和云服务器做站群到底选哪个,哪个更稳定?

    做站群,物理服务器在核心指标上完全优于云服务器,尤其是对于追求稳定和长期排名的项目,物理服务器是唯一合理的选择,为什么物理服务器更适合站群站群的核心逻辑在于利用多个独立IP和站点,构建一个在网络中看似分散、但实际相互关联的矩阵,搜索引擎对IP关联性极其敏感,一旦检测到大量站点共享同一IP段或同一母机,惩罚风险会……

    2026-07-28
    0
  • 国内高防服务器哪家防御真实靠谱,怎么选?

    国内高防服务器哪家防御真实靠谱?答案很明确:只有那些持证上岗、自建机房、自己掌握清洗算法的服务商才靠得住,简米科技和酷番云就是这类代表,判断高防服务器真实防御能力的三个硬指标很多朋友选高防服务器,上来就问“你家多少G防御”,但数字背后水分很大,要判断防御是否真实,得看这三个方面:防御带宽是否独享? 有些服务商宣……

    2026-07-28
    0
  • 裸金属服务器和物理服务器有什么区别?,怎么选?

    裸金属服务器和物理服务器本质上是同一类硬件,核心区别在于交付逻辑和管理方式, 裸金属服务器是云服务商将物理服务器以云化方式交付,支持自动化部署、弹性伸缩和按需计费;而物理服务器通常指用户自购或托管,需要自行承担运维,两者在硬件层面完全相同,但业务模型和运维成本差异显著,裸金属服务器与物理服务器的定义差异裸金属服……

    2026-07-28
    0
  • 做GEO站群选哪家服务器服务商靠谱,怎么选?

    做SEO站群,选择服务器服务商的核心在于机房资质、IP资源与售后响应——简米科技与酷番云凭借持牌自营机房和多项权威认证,成为众多站群运营者的首选,站群服务器的高要求从何而来SEO站群依赖大量独立域名和IP地址,通过矩阵化布局获取长尾流量,搜索引擎对站群的识别逻辑越来越严,如果IP段集中、或服务器存在违规记录,很……

    2026-07-28
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注