关于 Redis 中分布式锁

news2024/9/23 9:22:06

什么是分布式锁

在一个分布式系统中,也会涉及到多个节点访问同一个公共资源的情况。此时就需要通过锁来做互斥控制,避免出现类似于“线程安全”的问题。

而 Java 中的 synchronized 或者 C++ 中的 std::mutex,这样的锁都只能在当前进程中生效,在分布式的这种多个进程多个主机的场景下就无能为力了。此时就需要使用分布式锁。

分布式锁本质就是使用一个公共的服务器,来记录加锁状态。

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

基本原理

经久不衰的买票案例:

引入一个 Redis,作为分布式锁的管理器。

此时,如果服务器 1 尝试买票,就需要先访问 Redis,在 Redis 上设置一个键值对。

如果这个操作设置成功,就视为当前没有节点对该车次加锁,就可以进行数据库的读写操作。操作完之后,再把 Redis 上刚才的键值对删除。

如果在服务器 1 操作数据库的过程中,服务器 2 也来买票,也会尝试给 Redis 上写一个键值对,key 是同样的车次。但是此时设置的时候发现该车次的 key 已经存在了,则认为已经有其他服务器正在持有锁,此时服务器 2 就需要等待或者暂时放弃。

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

但是这个方式可以用来“加锁”,解锁可以通过 del 来删除键值对。但是可能在程序没有执行到“解锁”的时候,服务器就挂了。

引入过期时间

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

注意,这里的过期时间只能使用一个命令来设置。因为 Redis 中多个命令是不保证原子性的,或者说 Redis 的原子性就算执行失败也不会回滚。所以拆分成多个命令可能会出现一半成功,一半失败的情况,仍无法正确释放锁。

引入校验id

对于 Redis 中写入的加锁键值对,其他的节点也是可以删除的。为了解决这个问题,可以引入一个校验id。

比如可以把设置的键值对的值,不再是简单地设为一个 1,而是设置成服务器的编号,比如“001”:“服务器 1”。这样就可以在删除 key 的时候,先校验当前删除 key 的服务器是否是当初加锁的服务器,如果是才能真正删除,否则不能删除。

引入lua

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

Lua的语法类似于JS,是⼀个动态弱类型的语⾔.Lua的解释器⼀般使⽤C语⾔实现.Lua语法简单精炼,执⾏速度快,解释器也⽐较轻量(Lua解释器的可执⾏程序体积只有200KB左右).

因此Lua经常作为其他程序内部嵌⼊的脚本语⾔.Redis本⾝就⽀持Lua作为内嵌脚本.

可以将解锁的指令编写成 Lua 脚本代码,有 redis-cli 或者 redis-plus-plus 或者 jedis 等客户端加载,并发送给 Redis 服务器,由 Redis 服务器来执行这段逻辑。一个 lua 脚本会被 Redis 服务器以原子的方式来执行。

引入 watch dog(看门狗)

在设置了 key 的过期时间之后,仍然存在一定的可能性,当任务还没执行完,key 就先过期了,就导致锁提前失效。而如果设置的时间太长,万一对应的服务器挂了,此时其他服务器也不能及时获取到锁。

所以相比于设置一个固定的长时间,不如动态地调整时间更合适。

所谓的 watch dog,本质上是加锁的服务器上的一个单独的线程,通过这个线程来对锁过期时间进行“续约”。注意,这个线程是业务服务器上的,不是 Redis 服务器的。

举个例子:

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

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

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

  • 如果任务未完成,则把过期时间重新设置为 10s

这样就不用担心锁提前失效的问题了,而且如果服务器挂了,看门狗线程也就挂了,此时无人续约,这个 key 自然就可以迅速过期,让其他服务器能够获取到锁了。

引入 Redlock 算法

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

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

为了解决这个问题,Redis 的作者提出了 Redlock 算法。

引入一组Redis节点.其中每⼀组Redis节点都包含⼀个主节点和若干从节点.并且组和组之间存储的数据都是⼀致的,相互之间是"备份"关系(而并非是数据集合的⼀部分,这点有别于Rediscluster).

加锁的时候,按照⼀定的顺序,写多个master节点.在写锁的时候需要设定操作的"超时时间".比如50ms.即如果setnx操作超过了50ms还没有成功,就视为加锁失败.

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

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

这样的话,即使有某些节点挂了,也不影响锁的正确性。虽然还是有可能出现大多数节点都挂了的情况,但比较概率太小,忽略不计。

同理,释放锁的时候,也需要把所有节点都进行解锁操作。(即使是之前超时的节点,也要尝试解锁,尽量保证逻辑严密)。

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

分布式中任何一个节点都是不可靠的,最终的加锁成功结论是“少数服从多数”。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1932547.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

allure_pytest:AttributeError: ‘str‘ object has no attribute ‘iter_parents‘

踩坑记录 问题描述: 接口自动化测试时出现报错,报错文件是allure_pytest库 问题分析: 自动化测试框架是比较成熟的代码,报错也不是自己写的文件,而是第三方库,首先推测是allure_pytest和某些库有版本不兼…

Hadoop3:RPC通信原理及简单案例实现

一、场景介绍 我们知道,Hadoop中存在多种服务,那么,服务之间是如何通信的了? 比如,DN和NN之间如何通信? 这里,实际上是通过RPC实现进程间通信的了。 RPC属于Java网络编程范畴 需要编写客户端和…

Apache POI 使用Java处理Excel数据 进阶

1.POI入门教程链接 http://t.csdnimg.cn/Axn4Phttp://t.csdnimg.cn/Axn4P建议&#xff1a;从入门看起会更好理解POI对Excel数据的使用和处理 记得引入依赖&#xff1a; <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactI…

Java中锁的全面详解(深刻理解各种锁)

一.Monitor 1. Java对象头 以32位虚拟机位例 对于普通对象,其对象头的存储结构为 总长为64位,也就是8个字节, 存在两个部分 Kclass Word: 其实也就是表示我们这个对象属于什么类型,也就是哪个类的对象.而对于Mark Word.查看一下它的结构存储 64位虚拟机中 而对于数组对象,我…

阿里云开源 Qwen2-Audio 音频聊天和预训练大型音频语言模型

Qwen2-Audio由阿里巴巴集团Qwen团队开发&#xff0c;它能够接受各种音频信号输入&#xff0c;对语音指令进行音频分析或直接文本回复。与以往复杂的层次标签不同&#xff0c;Qwen2-Audio通过使用自然语言提示简化了预训练过程&#xff0c;并扩大了数据量。 喜好儿网 Qwen2-Au…

六边形动态特效404单页HTML源码

源码介绍 动态悬浮的六边形,旁边404文字以及跳转按钮,整体看着像科技二次元画风,页面简约美观,可以做网站错误页或者丢失页面,将下面的代码放到空白的HTML里面,然后上传到服务器里面,设置好重定向即可 效果预览 完整源码 <!DOCTYPE html> <html><head…

【VScode】安装【ESP-IDF】插件及相关工具链

一、ESP-IDF简介 二、VScode安装ESP-IDF插件 三、安装ESP-IDF、ESP-IDF-Tools以及相关工具链 四、测试例程&编译烧录 一、ESP-IDF简介 二、VScode安装ESP-IDF插件 【VScode】安装配置、插件及远程SSH连接 【VSCode】自定义配置 打开VScode&#xff0c;在插件管理搜索esp…

关于Linux的面试题(实时更新中~)

一、软连接和硬连接的区别&#xff1a; 软连接创建方式 ln -s 被链接文件 链接文件 &#xff08;1&#xff09;软链接是一个链接文件&#xff1b; &#xff08;2&#xff09;软链接有着自己的 inode 号&#xff08;文件编号&#xff09;&#xff1b; &#xff08;3&#…

怎么压缩pdf文件大小?分享8款便捷的PDF压缩工具分享

当用户上传PDF文件到网站时&#xff0c;常常会遇到文件大小的限制问题。尤其是当PDF文件包含大量图片、图形和丰富内容时&#xff0c;文件体积会变得很大。这时&#xff0c;为了符合网站的大小要求并成功上传&#xff0c;我们需要对PDF文件进行压缩。那么&#xff0c;如何将PDF…

NXP i.MX8系列平台开发讲解 - 3.19 Linux TTY子系统(二)

专栏文章目录传送门&#xff1a;返回专栏目录 Hi, 我是你们的老朋友&#xff0c;主要专注于嵌入式软件开发&#xff0c;有兴趣不要忘记点击关注【码思途远】 目录 1. Linux 串口驱动 1.1 Uart 驱动注册流程 1.2 uart 操作函数 1.3 line discipline 2. Linux tty应用层使用…

持续集成07--Jenkins配置Allure测试报告

前言 在持续集成&#xff08;CI&#xff09;流程中&#xff0c;自动化测试报告是评估软件质量和追踪问题的重要工具。Allure Framework作为一个轻量级且功能丰富的测试报告工具&#xff0c;能够生成详细的测试报告&#xff0c;帮助团队更好地理解测试结果。本章节“持续集成07-…

Spring Boot集成kudu快速入门Demo

1.什么是kudu 在Kudu出现前&#xff0c;由于传统存储系统的局限性&#xff0c;对于数据的快速输入和分析还没有一个完美的解决方案&#xff0c;要么以缓慢的数据输入为代价实现快速分析&#xff0c;要么以缓慢的分析为代价实现数据快速输入。随着快速输入和分析场景越来越多&a…

06MFC之对话框--重绘元文件

文章目录 实现示例展示需要绘制的窗口/位置控件位置更新下一次示例粗细滑动部分更新重绘元文件(窗口变化内容消失)方法一:使用元文件方法二:兼容设备方法三:使用自定义类存储绘图数据除画笔外功能处理画笔功能处理保存前面画的线及色彩实现示例展示 需要绘制的窗口/位置 …

Kafka Producer发送消息流程之消息异步发送和同步发送

文章目录 1. 异步发送2. 同步发送 1. 异步发送 Kafka默认就是异步发送&#xff0c;在Main线程中的多条消息&#xff0c;没有严格的先后顺序&#xff0c;Sender发送后就继续下一条&#xff0c;异步接受结果。 public class KafkaProducerCallbackTest {public static void mai…

k8s集群 安装配置 Prometheus+grafana+alertmanager

k8s集群 安装配置 Prometheusgrafanaalertmanager k8s环境如下&#xff1a;机器规划&#xff1a; node-exporter组件安装和配置安装node-exporter通过node-exporter采集数据显示192.168.40.180主机cpu的使用情况显示192.168.40.180主机负载使用情况 Prometheus server安装和配置…

如何用AI交互数字人一体机,打造政务服务新名片?

如今&#xff0c;将“高效办成一件事”作为优化政务服务、提升行政效能的重要抓手&#xff0c;各地方为了促进政务服务由传统模式向数字化、智能化方向转变&#xff0c;纷纷在政务服务场景融合了AI交互数字人&#xff0c;实现“无人化、智慧化”导办、帮办、代办等模式&#xf…

深度学习程序环境配置

深度学习环境配置 因为之前轻薄本没有显卡跑不起来&#xff0c;所以换了台电脑重新跑程序&#xff0c;故记录一下配置环境的步骤及常见错误 本人数学系&#xff0c;计算机部分知识比较匮乏&#xff0c;计算机专业同学可以略过部分内容 深度学习环境配置 深度学习环境配置 CUD…

Vue脚手架安装(保姆级)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

WEB前端05-JavaScrip基本对象

JavaScript对象 1.Function对象 函数的创建 //方法一&#xff1a;自定义函数 function 函数名([参数]) {函数体[return 表达式] }//方法二&#xff1a;匿名函数 (function([参数]) {函数体[return 表达式] }); **使用场景一&#xff1a;定义后直接调用使用(只使用一次) (fun…

【Arduino IDE】安装及开发环境、ESP32库

一、Arduino IDE下载 二、Arduino IDE安装 三、ESP32库 四、Arduino-ESP32库配置 五、新建ESP32-S3N15R8工程文件 乐鑫官网 Arduino官方下载地址 Arduino官方社区 Arduino中文社区 一、Arduino IDE下载 ESP-IDF、MicroPython和Arduino是三种不同的开发框架&#xff0c;各自适…