当前位置: 首页 > 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…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...

SpringTask-03.入门案例

一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

相关类相关的可视化图像总结

目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系&#xff0c;可直观判断线性相关、非线性相关或无相关关系&#xff0c;点的分布密…...

PLC入门【4】基本指令2(SET RST)

04 基本指令2 PLC编程第四课基本指令(2) 1、运用上接课所学的基本指令完成个简单的实例编程。 2、学习SET--置位指令 3、RST--复位指令 打开软件(FX-TRN-BEG-C)&#xff0c;从 文件 - 主画面&#xff0c;“B: 让我们学习基本的”- “B-3.控制优先程序”。 点击“梯形图编辑”…...