Redis 分布式锁实现原理

1572 字
8 分钟
Redis 分布式锁实现原理

0. 前言#

Golang 分布式锁实现原理

1. redis 分布式锁实现原理#

1.1 redis 实现分布式锁#

1.2 过期时间不精准问题#

使用 setnx 命令设置锁时,通常会设置一个过期时间以防止死锁。然而,由于网络延迟或其他因素,准确说是“客户端视角与服务端视角的时钟脱节” 以及 “进程停顿(STW)”,实际的过期时间可能会有所偏差,导致锁提前释放或延迟释放,从而引发并发问题。

一旦发生一锁多持现象,锁的基本性质 —— 独占性遭到破坏。这个可以通过看门狗策略来缓解,但是不能完全解决。

1.3 数据弱一致性问题#

当 Redis 挂了,可能会导致锁的状态不一致。例如,如果一个客户端持有锁并且正在执行关键操作,而 Redis 突然崩溃,其他客户端可能会认为锁已经释放,从而同时进入临界区,导致数据不一致。

Redis 挂了导致的状态不一致,本质是主从异步复制导致的锁丢失。Etcd/红锁从存储侧解决了‘锁状态的强一致性’,但依然无法从逻辑侧解决因客户端 STW 导致的一锁多持,后者需配合 Fencing Token 实现终极一致。

2. redis 分布式锁实现源码#

xiaoxuxiansheng
/
redis_lock
Waiting for api.github.com...
00K
0K
0K
Waiting...

2.1 基本框架#

在 Redis sdk 之上,基于 Redis 连接池模式,封装了一个简易的 Redis 客户端。

redis-lock基本框架.png
redis-lock基本框架.png

接下来,以分布式锁使用方的进程 id + 协程 id 拼接生成一个字符串,作为使用方的身份标识;在执行加锁操作时,通过 redis 的 setNEX 操作,把生成的字符串设置为 val,并且设定好锁数据的过期时间;

在执行解锁操作时,通过 lua 脚本原子化执行两个步骤:

  1. get 操作获取 val,查看是否和当前使用方身份一致;
  2. 倘若身份校验通过,执行 del 操作删除锁数据;

在加锁/解锁操作之外,额外支持一个延长锁过期时间的操作. 该操作同样通过 lua 脚本实现,包括两个步骤:

  1. get 操作获取 val,查看是否和当前使用方身份一致;
  2. 倘若身份校验通过,执行 expire 完成锁数据的续期

redis-lock流程.png
redis-lock流程.png

3. watch dog 实现原理#

3.1 续约机制#

在加锁成功后,启动一个后台协程,定时检查锁的剩余过期时间。如果发现剩余时间低于某个阈值(例如过期时间的一半),则调用续约函数,延长锁的过期时间。

基本上可以看为就是在 Redis 中实现了一个 etcd 的租约机制。

也就是说 Etcd 租约是 watch 回调型使用的,Redis 锁是主动轮询型用的

3.2 redisson#

Redisson 是一个基于 Redis 的 Java 分布式锁实现,上文提到的续约机制就是仿照 Redisson 实现的。

redisson
/
redisson
Waiting for api.github.com...
00K
0K
0K
Waiting...

3.3 看门狗 watch dog#

watch dog.png
watch dog.png

下面聊聊看门狗模式的执行流程(其实和 etcd 的租约续约机制非常类似):

  • 在执行 redis 分布式锁的上锁操作时,通过 setNEX 指令完成锁数据的设置,携带了一个默认的锁数据过期时间
  • 确认上锁成功后,异步启动一个 watchDog 守护协程,按照锁默认过期时间 1/4 ~ 1/3 的节奏(可自由设置),持续地对锁数据进行 expire 续期操作
  • 在解锁成功后,会负责关闭 watchDog,回收协程资源. (由于看门狗续期操作会先检查锁的所有权再延期数据,因此实际上使用方只要删除了锁数据,续期操作就不会生效了. 回收看门狗协程是为了规避协程泄漏问题)

4. RedLock实现原理#

本文 1.3 小节提出了因 redis 走的是注重于高可用性的 AP 流派,因此存在数据弱一致的问题,进而导致 redis 分布式锁可能出现同时被多方持有的情况. 本章开始,基于 red lock 红锁机制尝试提出这个问题的解决方案。

4.1 多数派原则#

所谓多数派原则,就是做出一项决议之前,让所有的参与者进行投票表决,只有投赞同票的人数达到参与者总人数的一半以上成为多数派时,这项决议才被通过。

在红锁 RedLock 实现中,会基于多数派准则进行 CAP 中一致性 C 和可用性 A 之间矛盾的缓和,保证在 RedLock 下所有 redis 节点中达到半数以上节点可用时,整个红锁就能够正常提供服务。

4.2 红锁 redLock#

红锁 Redlock 全称 redis distribution lock,是 redis 作者 antirez 提出的一种分布式锁实现方案。加锁成功的判断条件和Raft中向Candidate节点发起投票选举Leader的过程类似,都是基于多数派原则。

在红锁的实现中:

  • 我们假定集群中有 2N+1个 redis 节点(通常将节点总数设置为奇数,有利于多数派原则的执行效率)
  • 这些 redis 节点彼此间是相互独立的,不存在从属关系
  • 每次客户端尝试进行加锁操作时,会同时对2N+1个节点发起加锁请求
  • 每次客户端向一个节点发起加锁请求时,会设定一个很小的请求处理超时阈值
  • 客户端依次对2N+1个节点发起加锁请求,只有在小于请求处理超时阈值的时间内完成了加锁操作,才视为一笔加锁成功的请求
  • 过完2N+1个节点后,统计加锁成功的请求数量
  • 倘若加锁请求成功数量大于等于N+1(多数派),则视为红锁加锁成功
  • 倘若加锁请求成功数量小于N+1,视为红锁加锁失败,此时会遍历2N+1个节点进行解锁操作,有利于资源回收,提供后续使用方的取锁效率

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Redis 分布式锁实现原理
https://www.lansganbs.cn/posts/go源码/redis-分布式锁实现原理/
作者
Zowely
发布于
2026-01-25
许可协议
CC BY-NC-SA 4.0

评论区

Profile Image of the Author
Zowely
红叶最多情,一舞寄相思。
公告
欢迎来到我的博客!这里分享计算机等相关内容。