关于redis中的分布式锁

目录

分布式锁的基础实现

引入过期时间

引入校验id

引入lua脚本

引入看门狗

redlock算法


分布式锁的基础实现

多个线程并发执行的时候,执行的先后顺序是不确定的,需要保证程序在任意执行顺序下,执行逻辑都是ok的。

在分布式系统中,每个服务器都是独立的进程,因此,之前的锁,就难以对现在分布式系统中的多个进程之间产生制约。分布式系统中,多个进程之间的执行顺序也是不确定的。

像这样的情况就需要引入分布式锁来解决上述问题。

举个例⼦: 考虑买票的场景, 现在⻋站提供了若⼲个⻋次, 每个⻋次的票数都是固定的。现在存在多个服务器节点, 都可能需要处理这个买票的逻辑: 先查询指定⻋次的余票, 如果余票 > 0, 则设置余票值 -= 1。

显然上述的场景是存在 "线程安全" 问题的, 需要使⽤锁来控制。否则就可能出现 "超卖" 的情况。

此时如何进⾏加锁呢? 我们可以在上述架构中引⼊⼀个 Redis ,作为分布式锁的管理器。给其他的服务器提供"加锁"这样的服务。(redis是一种典型的可以用来实现分布式锁的方案,但不是唯一一种)

买票服务器,在进行买票的过程中,就需要先加锁(往redis上设置一个特殊的key-value)。完成上述买票操作之后,再把这个key-value删除掉。

其他服务器买票的时候,也要去redis上设置key-value,如果发现key-value已经存在,就认为"加锁失败"。(放弃/阻塞,就看具体的实现策略了)

上述操作就可以保证,第一个服务器执行"查询 ->更新"过程中,第二个服务器不会执行"查询"。

引入过期时间

如果服务器直接掉电,进程异常终止,这样的情况会导致redis上设置的key无人删除,也就导致其他服务器无法获取到锁了。这种情况该如何处理?

可以给key设置过期时间,一旦时间到,key就会自动被删除掉了。

可以通过 set ex nx 命令来实现。

比如设置key的过期时间为1000ms,那么意味着即使出现极端情况,某个服务器挂了,没有正确释放锁,这个锁最多保持1000ms,也就会自动释放了。

引入校验id

所谓的加锁,就是给redis上设置一个key-value。

所谓的解锁,就是给redis上这个key-value删除掉。

是否可能出现服务器1执行了加锁,服务器2执行了解锁

有可能的。服务器有可能不小心执行到了解锁操作,因此就可能进一步给整个系统带来更严重的问题。

为了解决上述问题,就需要引入校验机制

1.给服务器编号,每个服务器有一个自己的身份标识

2.进行加锁的时候,设置key-value,key对应着要针对哪个资源加锁(比如车次),value就可以存储刚才服务器的编号。表示出当前这个锁是哪个服务器加上的。

3.后续在解锁的时候,就可以进行校验了。(解锁的时候,先查询一下这个锁对应的服务器编号。然后判定一下这个编号是否就是当前解锁的服务器的编号,如果是,才能真正执行del。如果不是,就失败)

引入lua脚本

在解锁的时候,先查询判定,再进行del。此处两步操作(不是原子的),就可能会出现问题。

一个服务器内部,也可能是多线程的。此时,就可能同一个服务器,两个线程都在执行上述解锁操作。

在线程A执行完DEL之后,B执行DEL之前。服务器2的线程C正好要执行加锁(set),此时由于A已经把锁释放了,C的加锁是能够成功的。因为在线程B查询判定的时候,redis服务器已经判定这次请求来源于一个服务器了。但是紧接着,线程B DEL 就到来了。就把刚刚服务器2的加锁操作给解锁了。

可以使用lua脚本来解决上述问题。

可以使用lua编写一些逻辑,把这个脚本上传到redis服务器上。然后就可以让客户端来控制redis执行上述脚本了。

redis执行脚本的过程,也是原子的。相当于执行一条命令一样。

使⽤ Lua 脚本完成上述解锁功能:

复制代码
if redis.call('get',KEYS[1]) == ARGV[1] then
   return redis.call('del',KEYS[1])
else
   return 0
end;

引入看门狗

在加锁的时候,key的过期时间设定多长合适?

如果设置的短,可能在你的业务还执行完,就释放锁了。如果设置的太长,也会导致"锁释放不及时"问题。

更好的方式,是"动态续约"。初始情况下,设置一个过期时间(比如1s)就提前在还剩300ms(不一定,可以灵活调整)的时候,如果当前任务还没执行完,就把过期时间再续1s。等到时间又快到了,任务还没执行完,就再续。

如果服务器,中途崩溃了,自然就没人负责续约了,此时,锁就能在较短的时间内被自动释放。

动态续约这样的行为往往需要一个专门的线程,负责续约这个事情。称为"看门狗"。

redlock算法

使用redis作为分布式锁,redis本身有没有可能挂了呢? 当然有可能。

要想保证"高可用"就需要通过这样一系列的"预案演习"。

进行加锁,就是把key设置到主节点上,如果主节点挂了,有哨兵自动的把从节点升级成主节点,进一步的保证刚才的锁仍然可用。

但是主节点和从节点之间的数据同步,是存在延时的。可能主节点收到了set请求,还没来得及同步给从节点,主节点就先挂了。即使从节点升级成了主节点。但是,刚才的加锁对应的数据,也是不存在的。

Redlock 算法:

引⼊⼀组 Redis 节点。其中每⼀组 Redis 节点都包含⼀个主节点和若⼲从节点. 并且组和组之间存

储的数据都是⼀致的,相互之间是 "备份" 关系。

此处加锁,就是按照一定的顺序,针对这些组redis都进行加锁操作。如果某个节点挂了,继续给下一个加锁即可。

如果写入key成功的节点个数超过总数的一半,就视为加锁成功。

同理,进行解锁的时候,也会把上述节点都设置一遍。

以上,关于redis的分布式锁,希望对你有所帮助。

相关推荐
954L2 分钟前
docker安装milvus向量数据库&Attu可视化界面
数据库·docker·milvus·attu
SelectDB15 分钟前
MiniMax GenAI 可观测性分析 :基于阿里云 SelectDB 构建 PB 级别日志系统
大数据·数据库·aigc
专注_每天进步一点点21 分钟前
Redis客户端Jedis、Lettuce 和 Redisson优缺点总结
数据库·redis·缓存
果冻kk24 分钟前
【宇宙回响】从Canvas到MySQL:飞机大战的全栈交响曲【附演示视频与源码】
java·前端·数据库·spring boot·mysql·音视频·html5
别说我什么都不会42 分钟前
OpenHarmony源码分析之分布式软总线:os_adapter模块解析
分布式·harmonyos
桃酥4031 小时前
5、MySQL为什么使用 B+树 来作索引【高频】
数据库·b树·mysql
是阿建吖!2 小时前
【MySQL】基本查询(表的增删查改+聚合函数)
数据库·mysql
小玉起起2 小时前
什么是时序数据库?
数据库·时序数据库
绿龙术士2 小时前
【笔记】SQL进阶教程(第二版)
数据库
Hover_Z_快跑2 小时前
RabbitMQ 集群降配
分布式·rabbitmq