【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…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)
一、OpenBCI_GUI 项目概述 (一)项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台,其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言,首次接触 OpenBCI 设备时,往…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...