【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 是一个配置选项,用于处理索引模板中引用的组件模板可能不存在的情况。当您创建一个索引模板时,可以指定一个或多个组件模板࿰…...
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官方上也是如此 但是在我的系统(debian12)上却失败了: 报错有未满足的依赖关系: libc6 : 破坏: valgrind (< 1:3.19.0-1~) 但是 1:3.16.1-1 正要被安装 libc6-dbg : 依赖…...
vue3和vue2的区别有哪些差异点
Vue3 vs Vue2 主要差异对比指南 官网 1. 核心架构差异 1.1 响应式系统 Vue2:使用 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(六)(完结) 文章概括指导(Guidance)分类器指导无分类器引导(Classifier-Free Guidance) 总结 文章概括 引用: …...
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…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...

Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...

抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
2025.6.9总结(利与弊)
凡事都有两面性。在大厂上班也不例外。今天找开发定位问题,从一个接口人不断溯源到另一个 接口人。有时候,不知道是谁的责任填。将工作内容分的很细,每个人负责其中的一小块。我清楚的意识到,自己就是个可以随时替换的螺丝钉&…...