前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >再探 redis 分布式锁

再探 redis 分布式锁

作者头像
看、未来
发布2022-05-06 17:31:24
3690
发布2022-05-06 17:31:24
举报
文章被收录于专栏:CSDN搜“看,未来”

文章目录

前言

“分布式锁”这个话题在程序界有很大的关注量,引发了不少讨论。关于分布式锁有很多实现的方案,本文就讲基于 Redis 实现的分布式锁。一些基本的东西我就直接带过吧。

问:为什么需要分布式锁? 答:以前为什么需要互斥锁?

Redis 分布式锁的演进:

代码语言:javascript
复制
1、setnx
存在问题:若上锁的实例还没解锁就挂了,就死锁了。
解决方案:为锁设置一个过期时间。
代码语言:javascript
复制
2、setnx px,为锁设置一个过期时间。
存在问题:锁过期了,但是任务还没完成,锁就被释放了。当上锁者完成任务后,容易释放别的业务上的锁。

解决方案:可以采用看门狗进程,当锁要过期了就给它延时;也可以为锁设置一个token,只有token匹配上了才能解锁。
代码语言:javascript
复制
3、setnx key token px time
这里不采用看门狗进程的解法,具体可自己思考。

存在问题:若在解锁做 token 判断成功之后,解锁线程出现了调度,或延时,当真正操作 del 的时候锁已经过期了,则将把别的业务上的锁给解锁掉。

解决方案:将解锁过程写入 lua 脚本,将解锁过程原子化。调用 lua 脚本解锁。

lua 脚本如下:

代码语言:javascript
复制
--判断锁是自己的才释放
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else    
    return 0
end

到这里,一个单节点的 redis 锁便完成了它的使命。

但是,分布式到这里还没铺开呢!!!


主从切换

我们在使用 Redis 时,一般会采用主从集群 + 哨兵的模式部署。当主从发生切换的时候,这个分布式锁依旧安全么?

在这里插入图片描述
在这里插入图片描述

1、客户端1在主库上上锁。 2、主库宕机,setnx 命令还未到达从库。 3、从库被哨兵提升为新主库。 4、该锁在新主库上查不到,则可以由另一个客户端上锁。

于是又开始俄罗斯套娃了。

为此,Redis 的作者提出一种解决方案,就是我们经常听到的 Redlock(红锁)。


Redlock 红锁

Redlock 的方案基于 2 个前提: 1、不再需要部署从库和哨兵实例,只部署主库。 2、但主库要部署多个,官方推荐至少 5 个实例。

问:不做主从,那万一某个库宕机了,数据不就都丢了吗? 答:丢就丢了呗,本来就不是用来存数据的。

具体流程如下:

代码语言:javascript
复制
1、客户端先获取当前时间戳T1。
2、客户端依次向这 5 个 Redis 实例发起加锁请求,且每个请求会设置超时时间(毫秒级,要远小于锁的有效时间),
如果某一个实例加锁失败(包括网络超时、锁被其它人持有等各种异常情况),就立即向下一个 Redis 实例申请加锁。
3、如果客户端从 >=3 个(大多数)以上 Redis 实例加锁成功,则再次获取「当前时间戳T2」,如果 T2 - T1 < 锁的过期时间,此时,认为客户端加锁成功,否则认为加锁失败。
4、加锁成功,去操作共享资源。
5、加锁失败,向「全部节点」发起释放锁请求。

问:我也不贪心,我 5 个实例来共同竞争,需要竞争多少遍(失败一遍就要全部释放)? 答:emmm…emmm…emmm…(容我三思)(思不出来)

问:为什么要在多个节点上上锁? 答:防止某些个节点因为各种原因不能提供服务,且没来得及恢复。

问:为什么释放锁,要操作所有节点? 答:无则加勉,有则改之。

二三那俩问题是网上提的比较多的,问题一是我自己提的,结果自己答不上来。。。


分布式系统专家马丁的质疑

马丁写了一篇文章(How to do distributed locking),表达了自己对于红锁的质疑。在他的文章中主要阐述了四个观点: 1、分布式锁的目的是什么? 2、锁在分布式系统中会遇到的问题。 3、假设时钟正确是不合理的。 4、提出 fencing token 的方案,保证正确性。

我们一一来看:


1、分布式锁的目的。 他认为有两个目的:

1、效率。 2、正确性。

如果是为了效率,那么单机版的 Redis 就可以了,即使偶尔锁失效,也是可以理解的。 如果是为了正确性,马丁认为 Redlock 根本达不到分布式安全的要求,依旧存在锁失效的问题。

(其实我没想明白上锁提升效率,是什么情况。)


2、锁在分布式系统中会遇到的问题 Martin 表示,一个分布式系统,更像一个复杂的「野兽」,存在着你想不到的各种异常情况。

这些异常场景主要包括三大块,这也是分布式系统会遇到的三座大山:NPC。

代码语言:javascript
复制
N:Network Delay,网络延迟
P:Process Pause,进程暂停(GC)
C:Clock Drift,时钟漂移

Martin 用一个进程暂停(GC)的例子,指出了 Redlock 安全性问题:

代码语言:javascript
复制
1、客户端 1 请求锁定节点 A、B、C、D、E
2、客户端 1 的拿到锁后,进入 GC(时间比较久)
3、所有 Redis 节点上的锁都过期了
4、客户端 2 获取到了 A、B、C、D、E 上的锁
5、客户端 1 GC 结束,认为成功获取锁
6、客户端 2 也认为获取到了锁,发生「冲突」

Martin 认为,GC 可能发生在程序的任意时刻,而且执行时间是不可控的。

当然,即使是使用没有 GC 的编程语言,在发生网络延迟、时钟漂移时,也都有可能导致 Redlock 出现问题,这里 Martin 只是拿 GC 举例。


3、假设时钟正确是不合理的。

代码语言:javascript
复制
客户端C1获得了对节点A、B、c的锁定,由于网络问题,法到达节点D和节点E

节点C上的时钟向前跳,导致锁提前过期

客户端C2在节点C、D、E上获得锁定,由于网络问题,无法到达A和B

客户端C1和客户端C2现在都认为他们自己持有锁

前跳应该是不至于了,不过系统时钟是存在误差的,可以看我前面发的那篇: 计算机时钟是如何运行的?


4、提出 fencing token 的方案,保证正确性

代码语言:javascript
复制
1、客户端在获取锁时,锁服务可以提供一个「递增」的 token
2、客户端拿着这个 token 去操作共享资源
3、共享资源可以根据 token 拒绝「后来者」的请求

这样一来,无论 NPC 哪种异常情况发生,都可以保证分布式锁的安全性,因为它是建立在「异步模型」上的。

他还表示,一个好的分布式锁,无论 NPC 怎么发生,可以不在规定时间内给出结果,但并不会给出一个错误的结果。也就是只会影响到锁的「性能」(或称之为活性),而不会影响它的「正确性」。


Redis 作者 Antirez 的反驳

在 Redis 作者的文章中,重点有 3 个:

1)解释时钟问题。 Redis 作者表示,Redlock 并不需要完全一致的时钟,只需要大体一致就可以了,允许有「误差」。 (不晓得哦,前面不是要计算时间吗?)

2)解释网络延迟、GC 问题 Redis 作者强调:如果在 1-3 发生了网络延迟、进程 GC 等耗时长的异常情况,那在第 3 步 T2 - T1,是可以检测出来的,如果超出了锁设置的过期时间,那这时就认为加锁会失败,之后释放所有节点的锁就好了!

(但是如果是锁已经拿到手上了呢,拿到手之后GC)

  1. 质疑 fencing token 机制 第一,这个方案必须要求要操作的「共享资源服务器」有拒绝「旧 token」的能力。 第二,退一步讲,即使 Redlock 没有提供 fencing token 的能力,但 Redlock 已经提供了随机值(就是前面讲的 UUID),利用这个随机值,也可以达到与 fencing token 同样的效果。

我的思考

我想了好久,从上次接触 redis 分布式锁开始,因为没有思考清楚,我的毕设一直处于迟缓进度状态。

1、从生产实际出发。甘瓜苦蒂,天下物无全美。我们为什么要要求一套方案解决所有的问题呢?我只要这一套方案解决我的问题就行了,别人想借鉴,就借鉴呗,但不保证和他的需求百分百契合。

2、所以我的毕设决定采用:

代码语言:javascript
复制
setnx px 
	+
daemon
	+
fencing token
	+ 
lua

的方案,有几个点: 1、fencing token 实现方案:高并发下唯一 ID 生成方案 2、daemon 有限续命,如果占据锁的实例挂了,不会无休止的续命导致锁一直无法释放。且续命到达一定时长将写入日志,作为慢业务进行优化处理。 3、对于主从替换导致的分布式锁失效,做成 集群 + 主从,降低单节点被打崩的风险,也降低单节点承载的业务量。万一真被打崩了,不至于全军覆没。且由于 fencing token 的存在,可以将损失控制在很低的比率。 毕竟我的毕设里面实时数据快速变化的场景也就那么一两秒,平常流量不会那么大。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-01-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 前言
  • 主从切换
  • Redlock 红锁
  • 分布式系统专家马丁的质疑
  • Redis 作者 Antirez 的反驳
  • 我的思考
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档