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

Redis做分布式锁

(一)为什么要有分布式锁以及本质

  在一个分布式的系统中,会涉及到多个客户端访问同一个公共资源的问题,这时候我们就需要通过锁来做互斥控制,来避免类似于线程安全的问题

  因为我们学过的sychronized只能对线程加锁,在分布式系统中无法对进程进行加锁,此时我们就需要用分布式锁

 那分布式锁的本质我们可以理解为一个公共服务器,来记录加锁的状态,那我们这里使用redis来作为这个公共服务器

(二)分布式锁的基本实现

1.基础实现

  我们说分布式锁的本质是服务器,来记录加锁状态,那这个加锁的本质就是通过一个键值对的方式来标识锁的状态

  就用我们常见的购票场景,我们现在有多个服务器节点来提供购票需求,那购票的流程:先查询指定车的余票,如果余票>0,就-=1,很明显,这个操作不是原子的,就会导致有其他服务器节点插到中间执行,这就会导致超卖的情况(这种情况在单机上,我们可以通过对线程加锁的方式来处理,但是加锁对于多进程来说,是无能为力的)

  如果我们最终存储数据的服务器是mysql,我们可以使用事务来解决,但是如果不是,我们还是需要之后的步骤

  

 那我们这里想要加锁,我们就需要引入一个公共服务器,作为分布式锁的管理者

我们服务器节点会先访问redis,在redis设置一个键值对,比如key是车次

   如果这个操作设置成功,就视为有服务器节点对这个车次进行加锁了,其他的服务器节点想要购票时,就也需要写一个键值对,但是我们会发现这个key已经存在了此时就需要进行阻塞等待,等到我们加锁的这个服务器节点在进行完数据库的读写操作后,就要把redis刚才的键值对给删除掉,这个删除操作就相当于释放锁,然后我们其他的服务器节点才可以再次购票进行加锁解锁操作

 注:我们redis本来就是key-value结构,并且提供了setnx操作,当我们key不存在的时候就设置成功,存在就设置失败,所以使用redis很合适

2.引入过期时间

 当服务器1加锁后解锁前,如果服务器1挂了,就会导致我们这个解锁操作不能执行,那么其他的服务器程序就一直无法获取到锁

 那我们之前讲多线程的时候,使用了finally,来保证最后一定会解决,那这个能不能在分布式锁中使用呢?    是不可以的,因为我们加锁不是在自己进程中加的,而是在一个公共服务器中加的,程序异常退出,是不会给公共服务器上的锁解锁的

 那我们如果挂了,为了解决无法解锁的问题,不如我们未雨绸缪,我们在设置key的同时引入过期时间来保证这个key存在多久就自动销毁(这个锁加锁多久后自动解锁)

 那我们可以使用redis中的set ex nx的方式,在设置锁的同时设置过期时间

 注意:如果我们使用setnx 后使用expire,因为是两个指令,不是原子的,即使使用事务也不能保证都执行正确,就导致可能会出现无法正确释放锁的问题

3.引入校验id

   对于redis中的数据,我们其他服务器节点也是可以删除的,上述情况下,我们一个服务器对他加锁后,另一个服务器也可以进行解锁操作,虽然我们不会故意让另一个服务区程序去解锁,但是我们还是要保证不能因为一些bug导致服务器2把key误删了

  为了解决这个问题,我们可以把我们的服务器程序的编号给放到value中,因为我们上述说是写key然后校验key是否存在来判断是否加锁的,这个value的值就可以任我们发挥,那我们value放服务器编号,然后我们想要解锁时,先判断这个key对应的value是不是我们当前的服务器,如果是就进行删除,不是就不可以删除

那我们伪代码如下

 我们发现,这里的get和del不是原子的,那就可能会出现错误

  如果在多线程的情况下就会导致重复删除一个key,那有人会有疑问,重复删就删了呗,那如果此时正好有一个set key在最后一个del之前执行呢?这就会导致我们新设置的key被直接删除了

 那这里的问题我们可以使用redis事务来解决,也可以使用lua脚本

4.lua脚本

  lua是redis的一个内嵌语言,语法简单并且执行速度快,且轻量,并且redis官方文档说,我们可以使用lua来实现redis的事务

 因为redis执行lua脚本是相当于一个操作也就是原子的,但是lua脚本中可以有很多操作

如果使用lua脚本,那代码可以写为

5.引入watch dog

  上述方案还有一个问题,也就是我们过期时间的设置,我们设置了key过期时间后,可能会导致我们任务没执行完,key就过期了,或者当我们key过期时间很长,就会导致其他服务器无法及时加锁,那么针对这个问题,我们就引入了一个watch dog,本质上是加锁服务器上的一个单独线程,来针对这个加锁时间进行“续约”

 就比如,我们先设置过期时间1s,当还剩300ms我们任务没执行完,我们这个watch dog线程就会把过期时间重新设置,也就是续约过程,这样就不用担心过期时间设置的过长或者过短了,因为过期时间变成了动态的

 而且当我们服务器挂了,这个线程也就挂了,过期时间就不会改变,就可以正常的释放锁

6.引入redlock

我们上述都是在讨论服务器节点的问题,那假如我们redis服务器挂了呢?这时有人会说,主从结构和哨兵可以帮我们解决啊,我们一直会有主节点的,虽然是这样,但是会存在我们之前的主节点中有一些加锁操作没来得及同步数据给从节点,导致数据丢失,这样就相当于没有加锁,其他服务器就可以进行加锁

 那为了解决这种问题,redis作者提出了redlock算法

也就是引入了一组redis节点,每一组都是相同的主从结构,并且数据也相同,加锁的时候,我们按照一定顺序,写多个master节点,写操作时要设定操作的超时时间,当set key的操作超过超时时间没有成功,就视为加锁失败

   

  如果给某个节点加锁失败,就尝试下一个节点,当加锁成功的节点数超过总数一半,就视为加锁成功

 这个算法就是通过空间换取准确度

相关文章:

Redis做分布式锁

(一)为什么要有分布式锁以及本质 在一个分布式的系统中,会涉及到多个客户端访问同一个公共资源的问题,这时候我们就需要通过锁来做互斥控制,来避免类似于线程安全的问题 因为我们学过的sychronized只能对线程加锁&…...

lambdaQueryWrapper详细解释

LambdaQueryWrapper 是 MyBatis Plus 提供的一个强大的查询条件构建工具,它允许你使用 Lambda 表达式来构建查询条件,从而使代码更加简洁和易读。下面详细介绍 LambdaQueryWrapper 的使用方法及其底层原理。 什么是 LambdaQueryWrapper? La…...

【工控】线扫相机小结 第三篇

海康软件更新 目前使用的是 MVS_STD_4.3.2_240705.exe ,最新的已经到4.4了。 一个大的变动 在上一篇中我们提到一个问题: 需要注意的是,我们必须先设置 TriggerSelector 是 “FrameBurstStart” 还是 “LineStart” 再设置TriggerMode 是 …...

golang中的init函数

程序的初始化和执行都起始于 main 包。如果 main 包还导入了其它的包,那么就会在编译时将它们依次 导入。有时一个包会被多个包同时导入,那么它只会被导入一次(例如很多包可能都会用到 fmt 包,但 它只会被导入一次&#x…...

理解和选择Vue的组件风格:组合式API与选项式API详解

目录 前言1. Vue 的两种组件风格概述1.1 选项式 API:直观且分块清晰1.2 组合式 API:灵活且逻辑集中 2. 深入理解组合式 API 的特点2.1 响应式变量与函数式编程2.2 逻辑组织更清晰2.3 更好的代码复用 3. 应用场景分析:如何选择 API 风格3.1 适…...

Java基础——高级技术

1. 单元测试 就是针对最小的功能单元(方法),编写测试代码对其进行正确性测试。 1.1. Junit单元测试框架 可以用来对方法进行测试,他是第三方公司开源出来的(很多开发工具都已经集成了Junit框架,如IDEA&a…...

什么是SSL VPN?其中的协议结构是怎样的?

定义:SSL VPN是以SSL协议为安全基础的VPN远程接入技术,移动办公人员使用SSL VPN可以安全、方便的接入企业内网,访问企业内网资源,提高工作效率。 SSL(Security Socket Layer)是一个安全协议,为…...

程序员高频率面试题-整理篇

Redis 除了做缓存,还能做什么? 分布式锁:通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。 限流:一般是通过 Redis Lua 脚本的方式来实现限流。 消息队列&#x…...

第二十二章 TCP 客户端 服务器通信 - TCP设备的OPEN和USE命令关键字

文章目录 第二十二章 TCP 客户端 服务器通信 - TCP设备的OPEN和USE命令关键字TCP设备的OPEN和USE命令关键字TCP设备的OPEN和USE命令关键字 第二十二章 TCP 客户端 服务器通信 - TCP设备的OPEN和USE命令关键字 TCP设备的OPEN和USE命令关键字 可以使用位置参数(如上所述)或关键…...

CSS 语法规范

基本语法结构 CSS 的基本语法结构包含 选择器 和 声明块,两者共同组成 规则集。规则集可以为 HTML 元素设置样式,使页面结构和样式实现分离,便于网页的美化和布局调整。 CSS 规则集的结构如下: selector {property: value; }选择器(Selector) 选择器用于指定需要应用…...

Linux开发常用命令

文章目录 开发常用命令包管理 网络操作用户和权限系统监控nohup和screen的区别 开发常用命令 Linux开发中常用的命令非常多,以下是一些基本且重要的命令,这些命令对于日常的开发工作流程至关重要: 文件和目录操作 ls:列出目录内…...

Linux第92步_如何编写“设备树”下的platform设备驱动

Linux字符设备驱动,新字符设备驱动和设备树下的GPIO驱动,都是配置IO引脚所使用的GPIO寄存器,驱动开发方式和裸机没啥区别。Limux内核提供了pinctrl和gpio子系统用于GPIO驱动,借助它可简化GPIO驱动开发。 对GPIO进行读写操作&#…...

从零开始学习 sg200x 多核开发之 eth0 MAC 地址修改

在 sophpi 中,默认网卡 eth0 的 MAC 地址未配置,是随机生成的。这样就会导致每次重启之后,MAC 地址会改变,从而导致通过 DHCP 获取 IP 地址每次也都在变化。 查看 MAC 地址 前文提到 eth0 自动使能并通过 DHCP 获取 IP 地址&…...

JMeter与大模型融合应用之JMeter日志分析服务化实战应用

JMeter与大模型融合应用之JMeter日志分析服务化 引言 在当今的互联网时代,网站和应用程序的性能直接影响到用户的体验和业务的成功。为了保证系统的稳定性和高效性,性能测试成为了软件开发过程中的一个重要环节。在这其中,Apache JMeter作为一款开源的性能测试工具,凭借其…...

AtCoder Beginner Contest 380(A-F)

比赛链接:AtCoder Beginner Contest 380(A-F) A - 123233 题意 给出一个数字 N N N,问这个数字中是否 1 1 1 恰好出现了 1 1 1 次, 2 2 2 恰好出现了 2 2 2 次, 3 3 3 恰好出现了 3 3 3 次。 数据范围 100000 ≤ N ≤ 99…...

多线程-阻塞队列

目录 阻塞队列 消息队列 阻塞队列用于生产者消费者模型 概念 实现原理 生产者消费者主要优势 缺陷 阻塞队列的实现 1.写一个普通队列 2.加上线程安全和阻塞等待 3.解决代码中的问题 阻塞队列 阻塞队列,是带有线程安全功能的队列,拥有队列先进…...

el-table合并单元格之后,再进行隔行换色的且覆盖表格行鼠标移入的背景色的实现

el-table 中有现成的隔行换色功能,只要增加 stripe 属性即可。但是如果有单元格合并的话,这个属性就不可用了。这时候我们就需要动点小心思了。 基于相同字段进行合并 单元格合并:基于表头中的某一列,具有相同值的个数相加进行合…...

java模拟键盘实现selenium上下左右键 table中的左右滚动条实现滚动

在这篇文章中,我们将学习如何使用Java编程语言模拟键盘输入,特别是模拟上下左右方向键的操作。这是一个很有趣的项目,尤其适合刚入行的开发者。我们将分步进行,接下来,我们会通过表格展示整个实现过程,然后…...

SDF,一个从1978年运行至今的公共Unix Shell

关于SDF 最近发现了一个很古老的公共Unix Shell服务器,这个项目从1978年运行至今,如果对操作系统,对Unix感兴趣,可以进去玩一玩体验一下 SDF Public Access UNIX System - Free Shell Account and Shell Access 注册方式 我一…...

前馈神经网络 (Feedforward Neural Network, FNN)

代码功能 网络定义: 使用 torch.nn 构建了一个简单的前馈神经网络。 隐藏层使用 ReLU 激活函数,输出层使用 Sigmoid 函数(适用于二分类问题)。 数据生成: 使用经典的 XOR 问题作为数据集。 数据点为二维输入&#xff…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

大数据学习栈记——Neo4j的安装与使用

本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

C++_核心编程_多态案例二-制作饮品

#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为&#xff1a;煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例&#xff0c;提供抽象制作饮品基类&#xff0c;提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

django filter 统计数量 按属性去重

在Django中&#xff0c;如果你想要根据某个属性对查询集进行去重并统计数量&#xff0c;你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求&#xff1a; 方法1&#xff1a;使用annotate()和Count 假设你有一个模型Item&#xff0c;并且你想…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...