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

Redis 单个与多节点如何实现分布式锁

分布式锁

在许多环境中,分布式锁是非常有用的原语,在这些环境中,不同的进程必须以互斥的方式操作共享资源。在应对并发问题时,Redis 客户端还可以通过加锁的方式,来控制并发写操作对共享数据的修改,从而保证数据的正确性。

从设计建模角度来看,这三个属性是有效使用分布式锁所需的最低保证。

  • 互斥性:在任何给定时刻,只有一个客户端可以持有锁。
  • 无死锁:最终即使锁定资源的客户端崩溃或分区,也始终可以获取锁。
  • 容错性:只要大多数 Redis 节点正常运行,客户端就可以获取和释放锁。

Redis属于分布式系统,当有多个客户端需要获取锁时,这个锁不能是某个客户端本地锁。这个锁是保存到共享的存储系统(Redis)中。可以被多个客户端共享访问、获取。 它是一个分布式锁。 不是客户端本地锁。

单实例分布式锁

Redis 使用键值对来保存锁变量,不同客户端发送的加锁和释放锁的操作请求, 这个变量名作为键值对的键,而锁变量的值,则是键值对的值: 要获取锁,方法如下: unique_value 是客户端的唯一标识,可以用一个随机生成的字符串来表示。 PX 30000 表示此锁会在30s后过期,为避免死锁问题。

  • 加锁操作: 使用 SET 命令的 NX 和 EX/PX 选项。使用随机值是为了以安全的方式释放锁。当返回 OK 表示加锁成功。
# 仅当密钥尚不存在时,该命令才会设置密钥(NX选项),过期时间为 30000 毫秒(PX选项
SET distributed_lock unique_value NX PX 30000# 以下测试使用 uuidgen 生成一个随机字符串
root@ubuntu-x64_01:/opt# redis-cli --no-auth-warning -h 192.168.88.11 -p 6380 -a "******" SET distributed_lock $(uuidgen) NX PX 30000
OKroot@ubuntu-x64_01:/opt# redis-cli --no-auth-warning  -h 192.168.88.11 -p 6380 -a "******" get distributed_lock 
"0cde222a-bf17-4286-9a58-a886a8a8a716"

  • 解锁操作:使用 lua 脚本执行解锁操作,仅当密钥存在且存储在密钥中的值正是我期望的值时才删除该密钥。当返回 1 表示解锁成功。
if redis.call("get",KEYS[1]) == ARGV[1] thenreturn redis.call("del",KEYS[1])
elsereturn 0
end# 使用 lua 脚本执行解锁操作, 持有锁客户端解锁成功返回1
root@ubuntu-x64_01:/opt# redis-cli --no-auth-warning -h 192.168.88.11 -p 6380 -a "******" --eval /opt/distributed_unlock.lua distributed_lock , 0cde222a-bf17-4286-9a58-a886a8a8a716
(integer) 1# 使用 lua 脚本执行解锁操作, 未持有锁客户端解锁失败返回0
root@ubuntu-x64_01:/opt#  redis-cli --no-auth-warning -h 192.168.88.11 -p 6380 -a "******" --eval /opt/distributed_unlock.lua distributed_lock , 3e8a7cf1-424c-4bb8-ae6e-29f583c2
(integer) 0

在这里插入图片描述

上面的加锁操作,为避免出现死锁。我们使用了 SET 命令的 NX 和 EX/PX 选项,而不是使用 SETNX + EXPIRE ,因为 Redis 扩展了 SET 命令的参数,用这一条命令就可以实现原子性操作,否则需要考虑用LUA脚本。

“锁有效期”是我们用作密钥生存时间的时间。它既是自动释放时间,也是客户端在另一个客户端能够再次获取锁之前执行所需操作的时间,而不会在技术上违反互斥保证,互斥保证仅限于给定的窗口从获取锁的那一刻起的时间。

在释放锁操作时,客户端需要判断当前锁变量的值是否和自己持有锁的值相等。如果相等才会执行解锁操作。这样避免误释放锁的问题。

释放锁操作的逻辑包含读取锁变量、判断值是否相等、删除锁变量多个操作。为保证原子性执行,在执行时使用了LUA脚本,从而保证了锁释放操作的原子性。

分布式锁加锁和释放锁的过程,涉及多个操作,我们需要保证这些锁操作的原子性。

由于单实例节点存在单点故障,所以是否可以通过主从复制(哨兵模式),来实现分布锁的高可用? 当主节点不可用时,切换为新的主节点以维护分布锁变量 ?

这是不可行的。通过这样做,我们无法实现互斥的安全属性,因为 Redis 复制是异步的。
这是不可行的。通过这样做,我们无法实现互斥的安全属性,因为 Redis 复制是异步的。
这是不可行的。通过这样做,我们无法实现互斥的安全属性,因为 Redis 复制是异步的。

该模型存在竞争条件:

客户端1获取master中的锁。在对密钥的写入传输到副本之前,主服务器崩溃了。副本被提升为主节点。
客户端2获取对 1 已持有锁的同一资源的锁。违反安全!
有时,在特殊情况下,例如在故障期间,多个客户端可以同时持有锁是完全可以的。如果是这种情况,您可以使用基于复制的解决方案。

在这里插入图片描述

多实例分布式锁

Redlock 算法
该算法基本思路,假设有 5 个 Redis 主节点。这些节点是完全独立的,这里实例说明下是完全独立的,没有主从关系的。(需要在不同的计算机或虚拟机上运行 5 个 Redis master,以确保它们以基本独立的方式)。不使用复制或任何其他隐式协调系统。让客户端和多个独立的Redis依次请求加锁,如果客户端能够和半数以上的实例成功地完成加锁操作,则认为成功获得

为了获取锁,客户端执行以下操作:

  • Step 1: 它获取当前时间(以毫秒为单位)。

  • Step 2: 它尝试在所有 N 个实例中顺序获取锁,在所有实例中使用相同的密钥名称和随机值。在步骤 2 中,在每个实例中设置锁时,客户端使用比总锁自动释放时间较小的超时来获取锁。例如,如果自动释放时间为 10 秒,则超时可能在 5-50 毫秒范围内。这可以防止客户端在尝试与已关闭的 Redis 节点通信时长时间处于阻塞状态:如果某个实例不可用,我们应该尽快尝试与下一个实例通信。
    如果某个Redis实例发生故障了,为了保证Redlock算法有够继续运行,我们需要给加锁操作设置一个超时时间。

  • Step 3: 客户端通过从当前时间减去步骤 1 中获得的时间戳来计算获取锁所花费的时间。当且仅当客户端能够在大多数实例(至少 3 个)中获取锁时,并且获取锁所花费的总时间小于锁的有效时间,则认为获取了锁。

  • Step 4: 如果获取了锁,则其有效时间被视为初始有效时间减去经过的时间(如步骤 3 中计算的那样)。
    如果锁的有效时间已经来不及完成共享数据的操作了,我们可以释放锁,以免出现还没完成数据操作,锁就过期了的情况。

  • Step 5: 如果客户端由于某种原因未能获取锁(要么无法锁定 N/2+1 个实例,要么有效期为负),它将尝试解锁所有实例(甚至是它认为没有锁定的实例)能够锁定)。

该算法依赖于这样的假设:虽然进程之间没有同步时钟,但每个进程中的本地时间以大致相同的速率更新,与锁的自动释放时间相比,误差幅度很小。这种假设与现实世界的计算机非常相似:每台计算机都有一个本地时钟,我们通常可以依靠不同的计算机来获得很小的时钟漂移。

此时我们需要更好地指定我们的互斥规则:只有持有锁的客户端在锁有效时间内(如步骤3中获得的)终止其工作,减去一些时间(仅几毫秒),才可以保证。以补偿进程之间的时钟漂移)。

在这里插入图片描述

小结

1)分布式锁不能是某个客户端本地锁,是由共享系统维护的,而Redis作为共享存储系统可以实现分布式锁。
2)分布式锁加锁和释放锁的过程,涉及多个操作,我们需要保证这些锁操作的原子性。加锁通过一条命令SET 命令的 NX 和 EX/PX 选项实现原子操作。释放锁通过lua脚本实现原子操作
3)锁变量需要设置有效时间,避免客户端发生任何异常无法解锁的情况,导致死锁。
4)通过SET命令设置锁变量时,每个客户端使用唯一值,以免出现误释放操作。
5)利用分布锁完成大部分场景应用上层[互斥]目的。把并发请求阻挡在上层,减轻对要操作的资源压力。
6) 一个分布式锁,极端情况下锁会失败,不能假设分布式锁100%安全,对于敏感业务资源层要做好兜底。

相关文章:

Redis 单个与多节点如何实现分布式锁

分布式锁 在许多环境中,分布式锁是非常有用的原语,在这些环境中,不同的进程必须以互斥的方式操作共享资源。在应对并发问题时,Redis 客户端还可以通过加锁的方式,来控制并发写操作对共享数据的修改,从而保…...

频段划分学习射频知识的意义

一、射频电路设计与低频电路设计的不同点 随着频率提高,相应电磁波的波长与变得可与分立电路元件的尺寸相比拟时,电阻、电容和电感这些元件的电响应,将偏离他们的理想频率特性。以 WIFI 2.4G 频段为例,当频率为 2437MHz&#xff0…...

Effective Objective-C 学习(四)

掌握GCD及操作队列的使用时机 在执行后台任务时,GCD 并不一定是最佳方式。还有一种技术叫做 NSOperationQueue,它虽然与 GCD 不同,但是却与之相关,开发者可以把操作以 NSOperation 子类的形式放在队列中,而这些操作也…...

欢迎来到IT时代----盘点曾经爆火全网的计算机电影

计算机专业必看的几部电影 计算机专业必看的几部电影,就像一场精彩的编程盛宴!《黑客帝国》让你穿越虚拟世界,感受高科技的魅力;《社交网络》揭示了互联网巨头的创业之路,《源代码》带你穿越时间解救世界,这…...

光芒绽放:妙用“GLAD原则”打造标准的数据可视化图表

光芒绽放:妙用“GLAD原则”打造标准的数据可视化图表 文章目录 光芒绽放:妙用“GLAD原则”打造标准的数据可视化图表前言一、可视化工具有哪些?二、那如何做出正确可视化图表 ?GLAD原则1.G原则2.L原则3.A原则4.D原则 三、总结最后…...

如何设计出用于喜欢的界面

要设计出用户喜欢的界面,你可以考虑以下几个方面: 用户研究:首先要了解用户的需求和偏好。你可以通过用户调研、用户访谈和数据分析来获取这些信息。了解用户的行为模式、喜好和痛点,有助于设计出更吸引人的界面。 直观的布局&am…...

第三篇【传奇开心果系列】Python的文本和语音相互转换库技术点案例示例:pyttsx3实现语音助手经典案例

传奇开心果短博文系列 系列短博文目录Python的文本和语音相互转换库技术点案例示例系列 短博文目录一、项目背景和目标二、雏形示例代码三、扩展思路介绍四、与其他库和API集成示例代码五、自定义语音示例代码六、多语言支持示例代码七、语音控制应用程序示例代码八、文本转语音…...

JS中数组的常用方法

concat() 连接两个或更多的数组,并返回结果。 let array1 [1, 2, 3]; let array2 [4, 5, 6]; let concatenatedArray array1.concat(array2); console.log(concatenatedArray); // [1, 2, 3, 4, 5, 6]join() 把数组的所有元素放入一个字符串。元素通过指定…...

最好用的论文检索网站

网站展示: 网站链接 sci-hub文献检索 用途: 可以用文章的DOI来检索并下载文章...

AI专题:AI巨轮滚滚向前

今天分享的是电子系列深度研究报告:《AI专题:AI巨轮滚滚向前》。 (报告出品方:方正证券) 报告共计:65页 来源:人工智能学派 Gemini 1.5 Pro 性能显著增强,长上下文理解取得突破 …...

SpringBoot常见问题

1 引言 Spring Boot是一个基于Spring框架的快速开发脚手架,它简化了Spring应用的初始化和搭建过程,提供了众多便利的功能和特性,比如自动配置、嵌入式Tomcat等,让开发人员可以更加专注于业务逻辑的实现。   Spring Boot还提供了…...

五种多目标优化算法(MOAHA、MOGWO、NSWOA、MOPSO、NSGA2)性能对比,包含6种评价指标,9个测试函数(提供MATLAB代码)

一、5种多目标优化算法简介 1.1MOAHA 1.2MOGWO 1.3NSWOA 1.4MOPSO 1.5NSGA2 二、5种多目标优化算法性能对比 为了测试5种算法的性能将其求解9个多目标测试函数(zdt1、zdt2 、zdt3、 zdt4、 zdt6 、Schaffer、 Kursawe 、Viennet2、 Viennet3)&#xff…...

用 LangChain 和 Milvus 从零搭建 LLM 应用

如何从零搭建一个 LLM 应用?不妨试试 LangChain Milvus 的组合拳。 作为开发 LLM 应用的框架,LangChain 内部不仅包含诸多模块,而且支持外部集成;Milvus 同样可以支持诸多 LLM 集成,二者结合除了可以轻松搭建一个 LL…...

[Bug解决] Invalid bound statement (not found)出现原因和解决方法

1、问题描述 在写了一个很普通的查询语句之后,出现了下面的报错信息 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.xxx.oauth.mapper.WxVisitorQrBeanMapper.selectByComIdAndEmpId at org.apache.ibatis.binding.Mappe…...

Qt:Qt3个窗口类的区别、VS与QT项目转换

一、Qt3个窗口类的区别 QMainWindow:包含菜单栏、工具栏、状态栏 QWidget:普通的一个窗口,什么也不包括 QDialog:对话框,常用来做登录窗口、弹出窗口(例如设置页面) QDialog实现简易登录界面…...

uni-app判断不同端

大家好&#xff0c;今天给大家分享的知识是在uni-app中如何区分是在什么端操作的程序 话不多说直接上代码&#xff1a; // #ifdef APP-PLUS<view>APP端</view>// #endif// #ifdef H5<view>H5端</view>// #endif// #ifdef MP<view>小程序端</v…...

计算机网络-网络设备防火墙是什么?

一、防火墙基本概念 前面我们学习了交换机、路由器是网络中常用的设备&#xff0c;现实中还有一个很重要的设备-防火墙。防火墙这一设备通常用于两个网络之间有针对性的、逻辑意义上的隔离。在网络通信领域&#xff0c;防火墙是一种安全设备。它用于保护一个网络区域免受来自另…...

Code Composer Studio (CCS) - Breakpoint (断点)

Code Composer Studio [CCS] - Breakpoint [断点] 1. BreakpointReferences 1. Breakpoint 选中断点右键 -> Breakpoint Properties… Skip Count&#xff1a;跳过断点总数&#xff0c;在断点执行之前设置总数 Current Count&#xff1a;当前跳过断电累计值 References […...

人工智能_普通服务器CPU_安装清华开源人工智能AI大模型ChatGlm-6B_001---人工智能工作笔记0096

使用centos安装,注意安装之前,保证系统可以联网,然后执行yum update 先去更新一下系统,可以省掉很多麻烦 20240219_150031 这里我们使用centos系统吧,使用习惯了. ChatGlm首先需要一台个人计算机,或者服务器, 要的算力,训练最多,微调次之,推理需要算力最少 其实很多都支持C…...

分层钱包HD钱包

bc1 开头的通常指的是比特币&#xff08;Bitcoin&#xff09;的地址&#xff0c;这种格式遵循了比特币改进提案BIP 0173中定义的Bech32编码格式。Bech32地址也被称为"SegWit"地址&#xff0c;它们支持Segregated Witness功能&#xff0c;这是比特币网络为了提高区块链…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

学校招生小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码&#xff0c;专为学校招生场景量身打造&#xff0c;功能实用且操作便捷。 从技术架构来看&#xff0c;ThinkPHP提供稳定可靠的后台服务&#xff0c;FastAdmin加速开发流程&#xff0c;UniApp则保障小程序在多端有良好的兼…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

Typeerror: cannot read properties of undefined (reading ‘XXX‘)

最近需要在离线机器上运行软件&#xff0c;所以得把软件用docker打包起来&#xff0c;大部分功能都没问题&#xff0c;出了一个奇怪的事情。同样的代码&#xff0c;在本机上用vscode可以运行起来&#xff0c;但是打包之后在docker里出现了问题。使用的是dialog组件&#xff0c;…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...

怎么让Comfyui导出的图像不包含工作流信息,

为了数据安全&#xff0c;让Comfyui导出的图像不包含工作流信息&#xff0c;导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo&#xff08;推荐&#xff09;​​ 在 save_images 方法中&#xff0c;​​删除或注释掉所有与 metadata …...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...