当前位置:首页 > 问答 > 正文

Redis看门狗到底怎么保证系统安全,背后原理其实挺有意思的

Redis的看门狗,听起来像个保安,但它其实是一种非常巧妙的机制,专门用来解决分布式锁的一个核心难题:当加锁的客户端崩溃了,锁怎么安全地自动释放,避免把其他客户端永远堵在外面?

要理解看门狗为什么重要,我们得先看看没有它的时候,大家是怎么用Redis实现分布式锁的。

(来源:Redis官方文档对分布式锁的Redlock算法的描述,以及社区常见的实现讨论)

最常见的方式是使用SET key value NX PX milliseconds这个命令,简单说就是:“如果这个key不存在(NX),我就把它设成这个value,并且给它一个过期时间(PX),比如10秒”,这就相当于拿到了一个10秒有效期的锁。

业务代码拿到锁后,去执行那些不能多人同时干的事儿,比如扣减库存,执行完了,再发一个DEL key命令把锁删掉,相当于释放锁。

Redis看门狗到底怎么保证系统安全,背后原理其实挺有意思的

问题来了: 如果客户端A拿到锁之后,在执行业务逻辑的途中,突然崩溃了!或者网络出问题了!它就没法去执行那个DEL命令了,结果就是,这把锁会一直留在Redis里,直到10秒后自动过期,在这10秒内,其他所有客户端都无法再获得这把锁,整个系统就在那儿干等着,这就是所谓的“死锁”现象。

你可能会说:“那把过期时间设短一点不就行了?比如设成1秒。” 但这样也有风险,如果客户端A没崩溃,但它的业务逻辑比较复杂,1秒内根本没执行完,锁却自动过期了,这时客户端B就能拿到锁,然后A和B就同时操作同一份数据,锁就失去了意义,数据就乱套了。

(来源:对分布式锁常见陷阱的分析,特别是锁的持有时间与业务执行时间的不确定性矛盾)

这就是个两难的选择:过期时间设长了,客户端真崩溃时系统要阻塞很久;设短了,客户端没崩溃但业务慢,又会导致锁失效。

Redis看门狗到底怎么保证系统安全,背后原理其实挺有意思的

Redis看门狗就是为了解决这个“两难”而生的。

它的工作原理很像给锁买了一份“自动续期保险”,我们以一些常见的Redis客户端(比如Redisson)的实现为例来说明:

  1. 上锁与启动看门狗: 当客户端A成功加锁时,它不再设置一个固定的过期时间(或者先设一个较短的初始时间),紧接着,客户端A会在后台启动一个定时任务,这个定时任务就是“看门狗”。

  2. 定时续期: 这个看门狗任务会每隔一段时间(过期时间的三分之一),就去检查一下客户端A是否还持有这把锁,怎么检查呢?它会给Redis发送一个命令,延长这把锁的过期时间,如果锁的默认过期时间是30秒,那么看门狗会每隔10秒就去把锁的过期时间重新设置为30秒。

    Redis看门狗到底怎么保证系统安全,背后原理其实挺有意思的

  3. 维持心跳: 只要客户端A还“活着”,并且在正常执行业务逻辑,这个看门狗就会像心跳一样,不断地给锁“续命”,保证在业务完成之前,锁永远不会因为超时而被释放。

  4. 客户端崩溃,续期停止: 一旦客户端A崩溃了,它后台的那个看门狗定时任务自然也会随之停止运行,再也没有人去给锁续期了,锁在Redis里等待其当前剩余的过期时间(最多不超过一个续期间隔,比如10秒)结束后,就会自动被Redis删除释放。

这样一来,就完美地解决了之前的矛盾:

  • 安全性: 客户端崩溃后,锁最多再坚持10秒(而不是最初可能设的30秒或更长)就会释放,大大减少了系统阻塞时间。
  • 活性: 只要客户端不崩溃,无论业务逻辑执行多久(只要不超过看门狗进程本身的生存时间),锁都会一直被持有,避免了并发问题。

(来源:Redisson等客户端库的源码实现与设计文档)

你可以把Redis看门狗想象成一个忠诚的卫士,它待在加锁的客户端身边,不断对Redis大喊:“我的主人还活着,这把锁还要用!” 一旦这个卫士停止喊话,Redis就知道出事了,可以安全地把锁收回来给别人用。

看门狗机制也不是银弹,它自己也需要依赖客户端的运行环境,如果客户端不是“崩溃”,而是发生了长时间的GC停顿(一种程序本身暂停运行的情况),导致看门狗线程也无法工作,虽然进程没死,但锁也可能因为没及时续期而失效,不过在实际应用中,这种巧妙的“续期”思想,已经极大地提升了分布式锁的可靠性和健壮性,是保证系统安全稳定的一道重要防线。