当前位置: 首页 > news >正文

【redis进阶】分布式锁

目录

一、什么是分布式锁

二、分布式锁的基础实现

三、引入过期时间

四、引入校验 id

五、引入lua

六、引入 watch dog (看门狗)

七、引入 Redlock 算法

八、其他功能


redis学习🥳

一、什么是分布式锁

在一个分布式的系统中,也会涉及到多个节点访问同一个公共资源的情况. 此时就需要通过 锁 来做互

斥控制,避免出现类似于 "线程安全" 的问题.

而 java 的 synchronized 或者 C++ 的 std::mutex,这样的锁都是只能在当前进程中生效,在分布式

系统中,是有很多进程的(每个服务器,都是独立的进程)因此,之前的锁,就难以对现在分布式系

统中的多个进程之间产生制约。分布式系统中,多个进程之间的执行顺序也是不确定的 =>随机性,此

时就需要使用到 “分布式锁”.

✍ 本质上就是使用一个公共的服务器,来记录 加锁状态.

这个公共的服务器可以是 Redis,也可以是其他组件(比如 MySQL 或者 ZooKeeper 等),还可以是我们自己写的一个服务.

二、分布式锁的基础实现

思路非常简单. 本质上就是通过一个键值对来标识锁的状态.

举个例子: 考虑买票的场景,现在存在多个服务器节点,每个车次的票数都是固定的.

现在车站提供了若干个车次,都可能需要处理这个买票的逻辑: 先查询指定车次的余票,如果余票 > 0,

则设置余票值 -= 1.

显然上述的场景是存在 "线程安全" 问题的,需要使用锁来控制.

否则就可能出现 "超卖" 的情况.

此时如何进行加锁呢?我们可以在上述架构中引入一个 Redis 作为分布式锁的管理器.

此时,如果 买票服务器1 尝试买票,就需要先访问 Redis,在 Redis 上设置一个键值对. 比如 key 就是车次,value 随便设置个值 (比如 1).

如果这个操作设置成功,就视为当前没有节点对该 001 车次加锁,就可以进行数据库的读写操作. 操作

完成之后,再把 Redis 上刚才的这个键值对给删除掉.

如果在买票服务器1 操作数据库的过程中,买票服务器2 也想买票,也会尝试给 Redis 上写一个键值对

key 同样是车次. 但是此时设置的时候发现该车次的 key 已经存在了,则认为已经有其他服务器正在持

有锁,此时 服务器2 就需要等待或者暂时放弃.

🎉 Redis 中提供了 setnx 操作,正好适合这个场景. 即: key 不存在就设置,存在则直接失败.

但是上述方案并不完整.

三、引入过期时间

当 服务器1 加锁之后,开始处理买票的过程中,如果 服务器1 意外宕机了,就会导致解锁操作 (删除该

key) 不能执行. 就可能引起其他服务器始终无法获取到锁的情况.

为了解决这个问题,可以在设置 key 的同时引入过期时间. 即这个锁最多持有多久,就应该被释放.

🌰 可以使用 set ex nx 的方式,在设置锁的同时把过期时间设置进去.

注意! 此处的过期时间只能使用一个命令的方式设置.

如果分开多个操作,比如 setnx 之后,再来一个单独的 expire,由于 Redis 的多个指令之间不存在关联,并且即使使用了事务也不能保证这两个操作都一定成功,因此就可能出现 setnx 成功,但是 expire 失败的情况.

此时仍然会出现无法正确释放锁的问题.

四、引入校验 id

对于 Redis 中写入的加锁键值对,其他的节点也是可以删除的.

比如 服务器1 写入一个 "001": 1 这样的键值对,服务器2 是完全可以把 "001" 给删除掉的.

当然,服务器2 不会进行这样的 "恶意删除" 操作,不过不能保证因为一些 bug 导致 服务器2 把锁误删除.

为了解决上述问题,我们可以引入一个校验 id.

比如可以把设置的键值对的 value,不再是简单的设为一个 1,而是设成服务器的编号.

形如 "001":"服务器1" .

这样就可以在删除 key (解锁)的时候,先校验当前删除 key 的服务器是否是当初加锁的服务器,如果

是,才能真正删除;不是则不能删除。逻辑用伪代码描述如下:

String key = [要加锁的资源 id];
String serverId = [服务器的编号];// 加锁, 设置过期时间为 10s
redis.set(key, serverId, "NX", "EX", "10s");// 执行各种业务逻辑, 比如修改数据库数据.
doSomeThing();// 解锁, 删除 key. 但是删除前要检验下 serverId 是否匹配.
if (redis.get(key) == serverId) {redis.del(key);
}

但是很明显,解锁逻辑是两步操作 "get" 和 "del",这样做并非是原子的.

五、引入lua

为了使解锁操作原子,可以使用 Redis 的 Lua 脚本功能.

🎁 Lua 也是一个编程语言. 读作 "撸啊". 是葡萄牙语中的 "月亮" 的意思. (出自于 Lua 官方文档https://www.lua.org/about.html)

Lua 的语法类似于 JS,是一个动态弱类型的语言. Lua 的解释器一般使用 C 语言实现. Lua 语法

简单精炼,执行速度快,解释器也比较轻量 (Lua 解释器的可执行程序体积只有 200KB 左右).

因此 Lua 经常作为其他程序内部嵌入的脚本语言. Redis 本身就支持 Lua 作为内嵌脚本.

很多程序都支持内嵌脚本,比如 MySQL 8 支持 JS 作为内嵌脚本,比如 Vim 支持 VimScript

和 Python 作为内嵌脚本.... 通过内嵌脚本来实现更复杂的功能,提供更强的扩展性.

Lua 除了和 Redis 搭伙之外,在很多场景也会作为内嵌脚本. 比如在游戏开发领域常常作为编写逻辑的语言. (比如魔兽世界、大话西游等)

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

if redis.call('get',KEYS[1]) == ARGV[1] thenreturn redis.call('del',KEYS[1])
elsereturn 0
end;

上述代码可以编写成一个 .lua 后缀的文件,由 redis-cli 或者 redis-plus-plus 或者 jedis 等客户端加载,并发送给 Redis 服务器,由 Redis 服务器来执行这段逻辑.

一个 lua 脚本会被 Redis 服务器以原子的方式来执行.

redis-plus-plus jedis 如何调用 lua ,咱们此处不做过多介绍. 具体 api 的写法大家可以自行研究.

六、引入 watch dog (看门狗)

上述方案仍然存在一个重要问题. 当我们设置了 key 过期时间之后 (比如 10s),仍然存在一定的可能

性,当任务还没执行完,key 就先过期了. 这就导致锁提前失效.

把这个过期时间设置的足够长,比如 30s, 是否能解决这个问题呢?很明显,设置多长时间合适,是无止境的. 即使设置再长,也不能完全保证就没有提前失效的情况.

而且如果设置的太长了,万一对应的服务器挂了,此时其他服务器也不能及时的获取到锁.

因此相比于设置一个固定的长时间,不如动态的调整时间更合适.

所谓 watch dog,本质上是加锁的服务器上的一个单独的线程,通过这个线程来对锁过期时间进行 "续

约".

注意,这个线程是业务服务器上的,不是 Redis 服务器的.

❤ 举个具体的例子:

初始情况下设置过期时间为 10s. 同时设定看门狗线程每隔 3s 检测一次.

那么当 3s 时间到的时候,看门狗就会判定当前任务是否完成.

• 如果任务已经完成,则直接通过 lua 脚本的方式,释放锁(删除 key).

• 如果任务未完成,则把过期时间重写设置为 10s. (即 "续约")

这样就不担心锁提前失效的问题了. 而且另一方面,如果该服务器挂了,看门狗线程也就随之挂了,此

时无人续约,这个 key 自然就可以迅速过期,让其他服务器能够获取到锁了.

七、引入 Redlock 算法

实践中的 Redis 一般是以集群的方式部署的 (至少是主从的形式,而不是单机). 那么就可能出现以下比

较极端的大冤种情况:

🍰 服务器1 向 master 节点进行加锁操作. 这个写入 key 的过程刚刚完成,master 挂了;slave节点升级成了新的 master 节点. 但是由于刚才写入的这个 key 尚未来得及同步给 slave 呢,此时就相当于 服务器1 的加锁操作形同虚设了,服务器2 仍然可以进行加锁 (即给新的 master 写入 key. 因为新的 master 不包含刚才的 key).

因此之前学的 哨兵(分布式场景中,涉及的数据量不大)和集群(更多解决的是存储空间不足的问

题)模式 是不可取的,为了解决这个问题,Redis 的作者提出了 Redlock 算法.

我们引入一组 Redis 节点. 其中每一组 Redis 节点都包含一个主节点和若干从节点. 并且组和组之间存

储的数据都是一致的,相互之间是"备份"关系 (而并非是数据集合的一部分,这点有别于 Redis cluster).

加锁的时候,按照一定的顺序,写多个 master 节点. 在写锁的时候需要设定操作的 "超时时间". 比如

50ms. 即如果 setnx 操作超过了 50ms 还没有成功,就视为加锁失败.

如果给某个节点加锁失败,就立即再尝试下一个节点.

当加锁成功的节点数超过总节点数的一半,才视为加锁成功.

如上图,一共五个节点,三个加锁成功,两个失败,此时视为加锁成功.

这样的话,即使有某些节点挂了,也不影响锁的正确性.

🎼 那么是否可能出现上述节点都同时遇到了 "大冤种" 情况呢?理论上这件事是可能发生的,但是概率太小了. 工程上就可以忽略不计了.

同理,释放锁的时候,也需要把所有节点都进行解锁操作. (即使是之前超时的节点,也要尝试解锁,尽

量保证逻辑严密).

简而言之,Redlock 算法的核心就是,加锁操作不能只写给一个 Redis 节点,而要写个多个!分布式

系统中任何一个节点都是不可靠的. 最终的加锁成功结论是 "少数服从多数的".

由于一个分布式系统不至于大部分节点都同时出现故障, 因此这样的可靠性要比单个节点来说靠谱不少.

八、其他功能

上述描述中我们解释了基于 Redis 的分布式锁的基本实现原理.

上述锁只是一个简单的互斥锁. 但是实际上我们在一些特定场景中,还有一些其他特殊的锁,比如:

• 可重入锁

• 公平锁(遵循先来后到原则)

• 读写锁

• ......

基于 Redis 的分布式锁,也可以实现上述特性. (当然了对应的实现逻辑也会更复杂).

此处我们不做过多讨论了.

实际开发中,我们也并不会真的自己实现一个分布式锁. 已经有很多现成的库帮我们封装好了,我们直

接使用即可.

比如 Java 中的 Redisson,C++ 中的 redis-plus-plus. 当然,有些大厂也会有自己版本的分布式锁的实现.

redis学习打卡🥳

相关文章:

【redis进阶】分布式锁

目录 一、什么是分布式锁 二、分布式锁的基础实现 三、引入过期时间 四、引入校验 id 五、引入lua 六、引入 watch dog (看门狗) 七、引入 Redlock 算法 八、其他功能 redis学习🥳 一、什么是分布式锁 在一个分布式的系统中,也会涉及到多个节点访问同一…...

园区管理系统如何提升企业核心竞争力与资产管理智能化水平

内容概要 在当今快节奏的商业环境中,园区管理系统正成为企业的重要合作伙伴,尤其在工业园、产业园、物流园、写字楼和公寓等多种类型的物业管理中。这个系统不仅仅是一个管理工具,它还是提升企业运营效率和核心竞争力的关键因素。通过智能化…...

AI大模型开发原理篇-3:词向量和词嵌入

简介 词向量是用于表示单词意义的向量, 并且还可以被认为是单词的特征向量或表示。 将单词映射到实向量的技术称为词嵌入。在实际应用中,词向量和词嵌入这两个重要的NLP术语通常可以互换使用。它们都表示将词汇表中的单词映射到固定大小的连续向量空间中…...

高精度算法:高精度减法

P2142 高精度减法 - 洛谷 | 计算机科学教育新生态 我们两个整数一定要是大数减去小数,所以这个点我们需要特判一下,那我们两个字符串表示的整型怎么判断大小呢,我们字典序比较大小和真实的数字比较大小是一样的,比如我们的‘21’…...

Java创建项目准备工作

新建项目 新建空项目 每一个空项目创建好后都要检查jdk版本 检查SDK和语言级别——Apply——OK 检查当前项目的Maven路径,如果已经配置好全局,就是正确路径不用管 修改项目字符集编码,将所有编码都调整为UTF-8 创建Spingboot工程 创建Spring…...

基于STM32的智能宠物喂食器设计

目录 引言系统设计 硬件设计软件设计 系统功能模块 定时喂食模块远程控制与视频监控模块食物存量检测与报警模块语音互动与用户交互模块数据记录与智能分析模块 控制算法 定时与手动投喂算法食物存量检测与低存量提醒算法数据记录与远程反馈算法 代码实现 喂食控制代码存量检测…...

在线课堂小程序设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...

为AI聊天工具添加一个知识系统 之77 详细设计之18 正则表达式 之5

本文要点 昨天讨论了 本项目(AI聊天工具添加一个知识系统)中正则表达式模板的设计中可能要考虑到的一些问题(讨论到的内容比较随意,暂时无法确定 那些考虑 是否 应该是正则表达式模板设计要考虑的以及 是否完整)。今天…...

【Elasticsearch】 索引模板 ignore_missing_component_templates

解释 ignore_missing_component_templates 配置 在Elasticsearch中,ignore_missing_component_templates 是一个配置选项,用于处理索引模板中引用的组件模板可能不存在的情况。当您创建一个索引模板时,可以指定一个或多个组件模板&#xff0…...

Github 2025-01-29 C开源项目日报 Top10

根据Github Trendings的统计,今日(2025-01-29统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目10C++项目1Assembly项目1Go项目1我的电视 - 安卓电视直播软件 创建周期:40 天开发语言:CStar数量:649 个Fork数量:124 次关注人数:64…...

文件上传2

BUUCTF 你传你🐎呢 先上传.htaccess 修改格式 即可上传成功 返回上传图片格式的木马 用蚁剑连接 5ecf1cca-59a1-408b-b616-090edf124db5.node5.buuoj.cn:81/upload/7d8511a847edeacb5385299396a96d91/rao.jpg 即可得到flag [GXYCTF2019]BabyUpload...

Unity敌人逻辑笔记

写ai逻辑基本上都需要状态机。因为懒得手搓状态机,所以选择直接用动画状态机当逻辑状态机用。 架构设计 因为敌人的根节点已经有一个animator控制动画,只能增加一个子节点AI,给它加一个animator指向逻辑“动画”状态机。还有一个脚本&#…...

高级编码参数

1.跳帧机制 参考资料:frameskipping-hotedgevideo 跳帧机制用于优化视频质量和编码效率。它通过选择性地跳过某些帧并使用参考帧来预测和重建视频内容,从而减少编码所需的比特率,同时保持较高的视频质量。在视频编码过程中,如果…...

DeepSeek-R1:通过强化学习激励大型语言模型(LLMs)的推理能力

摘要 我们推出了第一代推理模型:DeepSeek-R1-Zero和DeepSeek-R1。DeepSeek-R1-Zero是一个未经监督微调(SFT)作为初步步骤,而是通过大规模强化学习(RL)训练的模型,展现出卓越的推理能力。通过强…...

leetcode——合并K个有序链表(java)

给你一个链表数组,每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中,返回合并后的链表。 示例 1: 输入:lists [[1,4,5],[1,3,4],[2,6]] 输出:[1,1,2,3,4,4,5,6] 解释:链表数组如下&#…...

【Valgrind】安装报错: 报错有未满足的依赖关系: libc6,libc6-dbg

Valgrind 内存泄漏检测工具安装 安装 sudo apt install valgrind官方上也是如此 但是在我的系统&#xff08;debian12)上却失败了&#xff1a; 报错有未满足的依赖关系&#xff1a; libc6 : 破坏: valgrind (< 1:3.19.0-1~) 但是 1:3.16.1-1 正要被安装 libc6-dbg : 依赖…...

vue3和vue2的区别有哪些差异点

Vue3 vs Vue2 主要差异对比指南 官网 1. 核心架构差异 1.1 响应式系统 Vue2&#xff1a;使用 Object.defineProperty 实现响应式 // Vue2 响应式实现 Object.defineProperty(obj, key, {get() {// 依赖收集return value},set(newValue) {// 触发更新value newValue} })Vue3…...

论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(六)(完结)

Understanding Diffusion Models: A Unified Perspective&#xff08;六&#xff09;&#xff08;完结&#xff09; 文章概括指导&#xff08;Guidance&#xff09;分类器指导无分类器引导&#xff08;Classifier-Free Guidance&#xff09; 总结 文章概括 引用&#xff1a; …...

NPM 使用介绍

NPM 使用介绍 引言 NPM(Node Package Manager)是Node.js生态系统中的一个核心工具,用于管理JavaScript项目的依赖包。无论是开发一个小型脚本还是构建大型应用程序,NPM都能极大地提高开发效率。本文将详细介绍NPM的使用方法,包括安装、配置、依赖管理、包发布等,帮助您…...

http3网站的设置(AI不会配,得人工配)

堡塔PHP项目中配置nginx1.26.0设置http3协议 # 文件所在服务器中的路径 /www/server/nginx/conf/nginx.confuser www www; worker_processes auto; error_log /www/wwwlogs/nginx_error.log crit; pid /www/server/nginx/logs/nginx.pid; worker_rlimit_nofile 512…...

Vue+Echarts 实现青岛自定义样式地图

一、效果 二、代码 <template><div class"chart-box"><chart ref"chartQingdao" style"width: 100%; height: 100%;" :options"options" autoresize></chart></div> </template> <script> …...

Java教程练习:学生信息管理系统

文章目录 学生管理系统1、需求文档需求分析 2、新建学生实体类3、实现基本菜单和退出功能4、查询和添加4.1 查询学生信息4.2 添加学生信息 5、修改和删除5.1 删除功能实现5.2 修改功能实现 完整代码下载 学生管理系统 1、需求文档 需求 采取控制台的方式书写学生管理系统。 …...

书生大模型实战营4

文章目录 L0——入门岛玩转HF/魔搭/魔乐社区HF 平台1 注册2 InternLM模型下载3 GitHub CodeSpace的使用4 Hugging Face Spaces的使用5 模型上传 魔搭社区平台简介创建开发机环境配置 下载指定多个文件上传模型 魔乐社区平台下载internlm2_5-chat-1_8b模型上传模型 始智AI平台下…...

麒麟操作系统服务架构保姆级教程(十四)iptables防火墙四表五链和防火墙应用案例

如果你想拥有你从未拥有过的东西&#xff0c;那么你必须去做你从未做过的事情 防火墙在运维工作中有着不可或缺的重要性。首先&#xff0c;它是保障网络安全的关键防线&#xff0c;通过设置访问控制规则&#xff0c;可精准过滤非法网络流量&#xff0c;有效阻挡外部黑客攻击、恶…...

8. 网络编程

网络的基本概念 TCP/IP协议概述 OSI和TCP/IP模型 socket&#xff08;套接字&#xff09; 创建socket 字节序 字节序转换函数 通用地址结构 因特网地址结构 IPV4地址族和字符地址间的转换(点分十进制->网络字节序) 填写IPV4地址族结构案例 掌握TCP协议网络基础编程 相关函数 …...

C++并发编程指南04

文章目录 共享数据的问题3.1.1 条件竞争双链表的例子条件竞争示例恶性条件竞争的特点 3.1.2 避免恶性条件竞争1. 使用互斥量保护共享数据结构2. 无锁编程3. 软件事务内存&#xff08;STM&#xff09; 总结互斥量与共享数据保护3.2.1 互斥量使用互斥量保护共享数据示例代码&…...

常见的同态加密算法收集

随着对crypten与密码学的了解&#xff0c;我们将逐渐深入学习相关知识。今天&#xff0c;我们将跟随同态加密的发展历程对相关算法进行简单的收集整理 。 目录 同态加密概念 RSA算法 ElGamal算法 ELGamal签名算法 Paillier算法 BGN方案 Gentry 方案 BGV 方案 BFV 方案…...

深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用

title: 深入探讨数据库索引类型:B-tree、Hash、GIN与GiST的对比与应用 date: 2025/1/26 updated: 2025/1/26 author: cmdragon excerpt: 在现代数据库管理系统中,索引技术是提高查询性能的重要手段。当数据量不断增长时,如何快速、有效地访问这些数据成为了数据库设计的核…...

记录 | Docker的windows版安装

目录 前言一、1.1 打开“启用或关闭Windows功能”1.2 安装“WSL”方式1&#xff1a;命令行下载方式2&#xff1a;离线包下载 二、Docker Desktop更新时间 前言 参考文章&#xff1a;Windows Subsystem for Linux——解决WSL更新速度慢的方案 参考视频&#xff1a;一个视频解决D…...

AI智慧社区--生成验证码

接口文档&#xff1a; 从接口文档中可以得知的信息&#xff1a; 代码的返回格式为json格式&#xff0c;可以将Controlller换为RestController前端发起的请求为Get请求&#xff0c;使用注解GetMapping通过返回的数据类型&#xff0c;定义一个返回类型Result package com.qcby.…...