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

Redis的分布式锁

目录

一、定义与原理

基于Redis的分布式锁

获取锁

释放锁

锁误删问题:因为key值一样,将别人的锁删掉了

锁误判问题二:判断锁和释放锁不是原子性的

Lua脚本


分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁

分布式锁的优点:

  • 多进程可见
  • 高可用
  • 安全性
  • 高性能
  • 互斥

知识点补充:

多进程(Multiprocessing)是操作系统中的一个重要概念,它指的是在操作系统中同时运行多个程序实例的能力。这些程序实例,即进程(Process),是系统进行资源分配和调度的一个独立单元,是程序在计算机上的一次执行活动。每个进程都有自己独立的内存空间和系统资源,它们之间通过特定的机制(如管道、消息队列、共享内存等)进行通信和数据交换。

多线程(Multithreading)是指从软件或者硬件上实现多个线程并发执行的技术。在计算机编程中,多线程是一种强大的工具,它允许程序能够同时执行多个任务,从而提高程序的执行效率和响应性。以下是对多线程的详细解释:

一、定义与原理

  • 定义:多线程是并行化的一种形式,通过拆分工作以便同时进行处理。在程序中,这些独立运行的程序片段被称作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。
  • 原理:多线程的并发执行机制原理是将一个处理器划分为若干个短的时间片,每个时间片依次轮流地执行处理各个线程,由于时间片非常短,相对于一个应用程序来说,就好像是处理器在为自己单独服务一样,从而达到多个应用程序或线程在同时进行的效果。

基于Redis的分布式锁

实现分布式锁时有两个要实现的基本方法:

获取锁

  • 互斥:确保只能有一个线程获取锁
  • 非阻塞:尝试一次,成功返回true,失败返回false
  • set lock thread ex 10 nx
  • 获取锁时存入线程标示(可以用UUID标示,UUID+ThreadId)

细节:如果第一步 setnx lock thread

第二部 expire lock 10

那么,如果redis在第一步执行完第二步执行前宕机了,就有造成死锁的概率

释放锁

  • 手动释放
  • 超时释放:获取锁时加一个超时时间
  • DEL KEY
  • 在释放锁时先获取锁中的线程标示,判断是否与当前的线程标示一致

一致释放锁,不一致不释放

锁误删问题:因为key值一样,将别人的锁删掉了

问题:线程1业务阻塞,redis锁超时已经释放,线程2拿到锁,但线程1去手动释放锁时,因为key一样,把线程2的锁给释放了,导致线程3拿到锁,同时就有两个线程拿到了锁。

解决:线程在手动释放锁的时候,判断一下这是不是自己上的锁,key的value值设为标示,UUID+ThreadId

  • ID_PREFIX 是一个静态的UUID前缀,这意味着它对于类来说是唯一的,但在多个JVM实例之间不是唯一的。不过,在这个上下文中,它主要用于与线程ID结合,以形成一个更长的、理论上唯一的标识符。
  • Thread.currentThread().getId() 获取当前线程的ID。这个ID在JVM内部是唯一的,但跨JVM则不是。

线程id:是JVM在创建线程时赋值的id,id是自增的,不同JVM的线程id可能相同

在这里锁对应的值不相同,因为值的前缀有UUID,不同的JVM不同的线程进来,都会创建锁对象上锁,每个锁对象在上锁时,都会生成一个随机的UUID作为value的前缀,所以key对应的值不同,id标示不同,就只能释放自己的锁。

未改进代码

public class SimpleRedisLock implements ILock{private String name;// 锁名称private final String KEY_PREFIX="lock:";private final String ID_PREFIX= UUID.randomUUID().toString(true)+"-";private StringRedisTemplate stringRedisTemplate;public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.name = name;this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean tryLock(long timeoutSec) {// 给线程id加个uuid前缀,避免在不同jvm下线程id相同,导致值相同String threadId = ID_PREFIX+Thread.currentThread().getId();Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId , timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(result);}@Overridepublic void unLock() {String threadId = ID_PREFIX+Thread.currentThread().getId();String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);if (threadId.equals(id)){stringRedisTemplate.delete(KEY_PREFIX+name);}}
}

锁误判问题二:判断锁和释放锁不是原子性的

在释放锁时,线程1已经判断完了,要去释放锁,但JVM垃圾回收时,遇到了阻塞,时间过久后,锁超时释放,线程2拿到锁开始执行业务,这时,线程1恢复正常,因为已经判断过锁是我上的了,它还会去释放锁,锁的key值都是 ->lock:业务:用户id,它找到线程2上的锁,并释放,线程3就可以来拿到锁,开始执行业务

Lua脚本

保证判断锁和释放锁原子性措施

Redis提供了Lua脚本功能,在一个脚本中编写多条Redis命令,确保多条命令执行时的原子性。Lua是一种编程语言

Redis提供的调用函数,语法如下:

redis.cail('命令名称','key','其它参数',...)

带参数的Lua语法

改进后的上锁和解锁

public class SimpleRedisLock implements ILock{private String name;// 锁名称private StringRedisTemplate stringRedisTemplate;private static final String KEY_PREFIX="lock:";// 不同jvm来加载这个类时,创建的uuid前缀不同,但这个uuid是静态的,无论对象创建多少次,都是唯一确定的private static final String ID_PREFIX= UUID.randomUUID().toString(true)+"-";private static final  DefaultRedisScript<Long> UNLOCK_SCRIPT;public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.name = name;this.stringRedisTemplate = stringRedisTemplate;}static {UNLOCK_SCRIPT = new DefaultRedisScript<>();UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));UNLOCK_SCRIPT.setResultType(Long.class);}@Overridepublic boolean tryLock(long timeoutSec) {// 给线程id加个uuid前缀,避免在不同jvm下线程id相同,导致值相同String threadId = ID_PREFIX+Thread.currentThread().getId();Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId , timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(result);}@Overridepublic void unLock() {// 执行lua脚本,来确保判断id标示和执行释放锁的命令的原子性stringRedisTemplate.execute(UNLOCK_SCRIPT,Collections.singletonList(KEY_PREFIX + name),ID_PREFIX+Thread.currentThread().getId());}
}

相关文章:

Redis的分布式锁

目录 一、定义与原理 基于Redis的分布式锁 获取锁 释放锁 锁误删问题&#xff1a;因为key值一样,将别人的锁删掉了 锁误判问题二&#xff1a;判断锁和释放锁不是原子性的 Lua脚本 分布式锁&#xff1a;满足分布式系统或集群模式下多进程可见并且互斥的锁 分布式锁的优点…...

C++笔记---类和对象

1. 类的定义 类是C中的一种自定义类型&#xff0c;是某个具体事物或概念的抽象化代码表示&#xff0c;通过类的成员&#xff08;变量函数/方法&#xff09;&#xff0c;可以表征出事物或概念的特征。 1.1 类定义的格式 class Stack { public:// 成员函数void Init(int n 4)…...

全国区块链职业技能大赛样题第9套后端源码

后端源码地址:https://blog.csdn.net/Qhx20040819/article/details/140746050 前端源码地址:https://blog.csdn.net/Qhx20040819/article/details/140746216 智能合约+数据库表设计:https://blog.csdn.net/Qhx20040819/article/details/140746646 项目预览 登录 用户管理...

3个功能强大的PDF转换工具,免费试用

给大家分享3个功能强大能满足更高需求的PDF转换工具&#xff0c;都提供免费的试用机会。 1.嗨动PDF编辑器 一款多功能的PDF编辑软件&#xff0c;集PDF阅读、PDF转换、PDF编辑功能为一体。支持转换的格式多样&#xff0c;转换速度快&#xff0c;转换后的排版和内容与原文保持一…...

表单修改数字输入框保留小数点

1.在表单设计打开修改的表单 2.打开需要修改的字段 3.按F12&#xff0c;进入“开发模式” 4.开启“开发者工具”左上角检索工具 开启&#xff1a;鼠标左键点击&#xff0c;当图标颜色为蓝色 关闭&#xff1a;鼠标左键点击&#xff0c;当图标颜色为灰色 5.鼠标移动到需要修改的…...

[VS Code扩展]写一个代码片段管理插件(一):介绍与界面搭建

文章目录 VS Code扩展机制项目搭建创建UI元素活动栏按钮主边栏视图主边栏工具栏按钮侧边栏右键菜单编辑器右键菜单 项目地址 [VS Code扩展]写一个代码片段管理插件&#xff08;一&#xff09;&#xff1a;介绍与界面搭建[VS Code扩展]写一个代码片段管理插件&#xff08;二&…...

vxe grid slots 用法

官方例子&#xff1a;Vxe Table v3.8 {field: num1,title: Num1,showHeaderOverflow: true,filters: [{ data: }],editRender: { autofocus: .my-input },slots: {// 使用插槽模板渲染default: num1_default,header: num1_header,footer: num1_footer,filter: num1_filter,edi…...

【网络】基于UDP协议的聊天室(第二篇)

目录 UDP服务器 UDP客户端 在C中&#xff0c;使用UDP协议进行网络通信通常涉及到socket编程。下面我将给出基于UDP的简单的客户端和服务器示例代码。这些示例将使用C标准库以及POSIX套接字接口&#xff08;主要适用于Linux和类Unix系统&#xff09;。如果你在使用Windows&…...

【SpringBoot3】场景整合(实战)

0 环境准备 0.0 云服务器 阿里云、腾讯云、华为云 服务器开通&#xff1b; 按量付费&#xff0c;省钱省心 安装以下组件&#xff1a;docker、redis、kafka、prometheus、grafana 下载windterm&#xff1a; https://github.com/kingToolbox/WindTerm/releases/download/2.5…...

【全网最全最详细】MYSQL 面试题大全(上)

目录 一、关系型数据库和非关系型数据库主要有哪些区别? 二、MYSQL的数据存储一定是基于硬盘的吗? 三、InnoDB和MyISAM有什么区别? 四、MyISAM的索引结构是怎么样的?存在的问题是什么? 五、char和varchar的区别? 六、MYSQL 5.x和8.0有什么区别? 七、为什么大厂不…...

【C语言】程序环境,预处理,编译,汇编,链接详细介绍,其中预处理阶段重点讲解

目录 程序环境 翻译环境 1. 翻译环境的两个过程 2. 编译过程的三个阶段 执行环境 预处理(预编译) 1. 预定义符号 2. #define 2.1 用 #define 定义标识符(符号) 2.2 用 #define 定义宏 2.3 #define 的替换规则 2.4 # 和 ## 的用法 2.5 宏和函数 2.6 #undef …...

人生低谷来撸C#--021 多线程

1、概念 线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及到复杂的和耗时的操作&#xff0c;那么设置不同的线程执行路径往往是有益的&#xff0c;每个线程执行特定的工作。 线程是轻量级进程。一个使用线程的常见实例是现代操作系统中…...

【优秀python django系统案例】基于python的医院挂号管理系统,角色包括医生、患者、管理员三种

随着信息技术的迅猛发展&#xff0c;传统的医院挂号管理方式面临着效率低下、排队时间长、信息不对称等诸多问题。这些问题不仅影响患者的就医体验&#xff0c;也加重了医院工作人员的负担。在此背景下&#xff0c;基于Python的医院挂号管理系统应运而生。该系统旨在通过信息化…...

硬盘数据丢失不再怕,四大恢复工具帮你轻松逆转局面!

硬盘故障、误删文件、病毒攻击等原因导致数据丢失的情况时有发生。面对这种情况&#xff0c;如何高效、快速地进行硬盘数据恢复呢&#xff1f;接下来几款好用的数据恢复软件推荐给大家。 一、福昕数据恢复&#xff1a;全方位恢复&#xff0c;让数据无遗漏 链接&#xff1a;ww…...

自定义封装日历组件

自定义日历 工作需要&#xff0c;但现有框架封装的日历无法满足需求&#xff0c;又找不到更好的插件&#xff0c;所以准备自己封装一个。 效果图和说明 一个很简易版的demo日历&#xff0c;本文只提供最基本的功能代码&#xff0c;便于阅读二开。 新建calendar.vue文件 <…...

【大模型】【面试】独家总结表格

问题解答你能解释一下Transformer架构及其在大型语言模型中的作用吗?Transformer架构是一种深度神经网络架构,于2017年由Vaswani等人在他们的论文“Attention is All You Need”中首次提出。自那以后,它已成为大型语言模型(如BERT和GPT)最常用的架构。 Transformer架构使用…...

C# 6.定时器 timer

使用控件&#xff1a; 开启定时器&#xff1a;timer1.Start(); 关闭定时器&#xff1a;timer1.Stop(); 定时间时间间隔:Interval timer1.Interval 1000; Interva等于1000是每一秒刷新一次 定时器默认时间间隔是100ms 代码创建定时器 ①创建 Timer t1 new Timer(); …...

有了 createSlice,还有必要使用 createReducer 吗?什么情况需要 createReducer 呢?

通常情况下&#xff0c;使用 createSlice 已经足够满足大多数需求&#xff0c;而不需要直接使用 createReducer。但是&#xff0c;在某些特定场景下&#xff0c;createReducer 仍然有其用处&#xff1a; 更细粒度的控制&#xff1a; 当你需要对 reducer 的行为进行更精细的控制…...

怎么搭建AI带货直播间生成虚拟主播?

随着电商直播带货的热潮不断升温&#xff0c;虚拟主播逐渐崭露头角&#xff0c;成为电商直播领域的新宠&#xff0c;相较于真人主播&#xff0c;虚拟主播具备无档期风险、人设稳定可控、24小时不间断直播等显著优势。 本文将深入探讨如何搭建一个AI带货直播间&#xff0c;并详…...

设计模式的原则

设计模式的原则通常包括以下几种核心原则&#xff1a; 单一职责原则 (SRP)&#xff1a;一个类应该只有一个单一的职责&#xff0c;即该类应该只有一个引起它变化的原因。这样可以减少类之间的耦合&#xff0c;使得系统更加易于维护和扩展。 开放/封闭原则 (OCP)&#xff1a;软…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

Linux-07 ubuntu 的 chrome 启动不了

文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了&#xff0c;报错如下四、启动不了&#xff0c;解决如下 总结 问题原因 在应用中可以看到chrome&#xff0c;但是打不开(说明&#xff1a;原来的ubuntu系统出问题了&#xff0c;这个是备用的硬盘&a…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文全面剖析RNN核心原理&#xff0c;深入讲解梯度消失/爆炸问题&#xff0c;并通过LSTM/GRU结构实现解决方案&#xff0c;提供时间序列预测和文本生成…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...