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

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

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

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

浅谈不同二分算法的查找情况

二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况&#xf…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

rnn判断string中第一次出现a的下标

# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) ​遍历字符串​:通过外层循环逐一检查每个字符。​遇到 ? 时处理​: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: ​与…...