Redis:SETNX解决分布式锁误删问题

news2024/9/25 23:17:53

Redis:SETNX解决分布式锁误删问题

    • 一.概述
    • 二. 分布式锁(初级)
      • (1)锁接口
      • (2)锁实现类+上锁
      • (3)释放锁
      • (4)存在的问题
    • 三. 改进释放锁
      • (1)准备unlock.lua脚本
      • (2)提前读取脚本
      • (3)实现释放锁

一.概述

前提:解决“一人一单”问题时,使用Redis互斥锁 锁住联合查询、扣库存、创建订单的步骤,防止同一个userID的多个线程去创建订单,最后在finally{ } 释放锁;

问题:出现锁的误删现象;

分析
当线程1获取锁,而此时线程1执行的业务因为某些情况被**阻塞**,阻塞的时间太长,TTL到期导致锁被释放;
这时线程2就能获取锁;线程2执行自己的业务;
假设此时线程1 阻塞完了,业务完成,执行DEL 释放锁的逻辑!则线程2的锁被释放了!(误删)
由于锁被删除,则线程3也能获取锁;此时就有两个线程并行执行!
在这里插入图片描述

原因
1.业务阻塞导致线程1的锁提前释放;
2.线程1释放了线程2的锁;

解决
上锁时 添加 线程标识;
解锁时 判断 线程标识
在这里插入图片描述

二. 分布式锁(初级)

需求
1.获取锁时的value使用 UUID + 线程ID 作为线程标识
2.释放锁时先获取锁中的线程标识,判断 于当前线程标识是否一致!
一致则释放锁;
不一致则不释放;

(1)锁接口

在这里插入图片描述

(2)锁实现类+上锁

使用 SETNX / setIfAbsent 作为互斥锁;
key 是传入的name;
value =UUID+线程ID(保证不易冲突),UUID使用hutool中的UUID.randomUUID.toString,会去掉默认的横线 “-”;

在这里插入图片描述

(3)释放锁

先通过key获取当前线程的线程标识(value);
然后判断当前锁中的线程标识是否等于锁的线程标识即value;
在这里插入图片描述

注意:这里的UUID是静态static修饰的,类加载的时候就会产生,同一个项目(JVM)内UUID相同,不同的项目(JVM)UUID则不同,这样就能防止线程ID作为标识存在不同JVM之间重复冲突的问题;

总结:通过上锁时添加线程标识解锁时判断线程标识,解决了锁误删,提高健壮性!

(4)存在的问题

依然可能产生误删!
线程1判断成功准备释放锁,此时线程1 阻塞,如GC中的stw;(判断锁和释放锁是两个动作)
当阻塞时间够长超过TTL,则锁超时释放
此时线程2可以获取锁,并执行业务;
此时线程1阻塞结束,而之前已经判断过锁了,线程1认为锁是自己的,所以直接释放锁,再次造成误删
在这里插入图片描述
原因:判断锁和释放锁是两个动作!应该有原子性!

三. 改进释放锁

由于判断锁和释放锁是两个动作,【当判断锁和释放锁之间】产生了阻塞,则会导致误删;

解决
使判断锁释放锁具有 原子性

需求:使用基于Lua脚本实现分布式锁的释放锁逻辑;
使用RedisTemplate中的Excute() 方法来执行Lua脚本,参数分别是:脚本、KEYS参数集合、ARGS参数集合;(不需要指定key的个数)

(1)准备unlock.lua脚本

KEY参数传入锁的key;
AVG参数传入当前线程标识;
在这里插入图片描述

并将unlock.lua 脚本放到项目的 resources 中下;
在这里插入图片描述

(2)提前读取脚本

为了避免在使用脚本的时候才去读脚本,造成IO流影响性能,使用DefaultRedisScript 读取脚本,使用静态代码块提前读取;
在这里插入图片描述

(3)实现释放锁

执行脚本实现释放锁,此时只有一行代码(在Lua脚本中),可以保证判断线程标识和释放锁的 原子性
使用RedisTemplate的 execute() 方法执行Lua脚本;
KEYS参数必须是集合,使用Collections.singletonList快速生成;

在这里插入图片描述

总结
利用SETNX 实现互斥性
利用EXPIRE 保证超时释放
使用Redis集群保证高可用,高并发;
运行Lua脚本保证原子性

key:将用户ID作为锁的key,保证同一个用户ID获取同一把锁,而不同用户之间不阻塞!
value:为了防止锁误删,使用线程标识作为value 以区别不同的线程,释放锁的时候先判断再释放;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/376320.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux:字符串拷贝的五种方法:使用指针下标,指针变量加偏移量,指针变量自加等

字符串数组名做函数形参&#xff0c;会退化正指针变量&#xff0c;需要使用指针变量操作字符串 代码&#xff1a; #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <s…

NDlib:npm启动网络演化可视化项目(SIR模型)

文章目录安装node.js以及vue启动NDlib_viz可视化项目后续问题打开项目安装node.js以及vue 参考之前的文章Node.js、npm和vue下载及安装 启动NDlib_viz可视化项目 在github下载项目到本地 https://github.com/GiulioRossetti/NDLib_viz 将node.js路径添加到环境变量 打开命令…

令执法机构头疼的“虚拟货币犯罪”,为何链上天眼能“行”

谈到洗钱&#xff0c;你脑海中率先想到的可能是影视剧中利用赌场、收藏品拍卖等来实施犯罪。其实洗钱犯罪的花样不止于此&#xff0c;在近期热播的扫黑剧《狂飙》中&#xff0c;唐小龙为洗白“赌博资金、高利贷业务”&#xff0c;便通过“卖酒网销”的方式达成洗钱目的。 随着科…

基于SpringCloud的可靠消息最终一致性04:项目基础代码

上一节给出了项目需求和骨架代码,这一节来接着看基础代码。骨架代码和基础代码最主要的区别是:骨架代码都是数据库脚本、POM依赖文件、配置文件内容、运维脚本等,而基础代码则是和业务有关联,但并非关键代码的部分。 这些代码不用一个个地看,主要是看看结构就行。 图二十五…

python 生成唯一id的实现方式

python 生成唯一id的实现方式 常用的python生成唯一id的四种方式 1.使用UUID UUID应该是大家耳熟能详的一个东西了&#xff0c;它的全称叫 通用唯一识别码&#xff08;英語&#xff1a;Universally Unique Identifier&#xff0c;缩写&#xff1a;UUID&#xff09;生成标准3…

对比多个笔记软件后,我选择了语雀

1、前言 早在两年前&#xff0c;那是我第一次用语雀&#xff0c;第一次使用时&#xff0c;就被其用户体验所震撼。它的操作界面简洁、清晰、易于上手&#xff0c;而且功能齐全、方便实用。但是那时候除了工作的笔记&#xff0c;大部分都还在印象笔记上面&#xff0c;也有部分文…

std::chrono笔记

文章目录1. radio原型作用示例2. duration原型&#xff1a;作用示例3. time_point原型作用示例4. clockssystem_clock示例steady_clock示例high_resolution_clock先说感觉&#xff0c;这个库真恶心&#xff0c;刚接触感觉跟shi一样&#xff0c;特别是那个命名空间&#xff0c;太…

人工智能高等数学--人工智能需要的数学知识_微积分_线性代数_概率论_最优化---人工智能工作笔记0024

然后我们看一下人工智能中需要的数学知识 数学知识是重要的,对于理解人工智能底层原理来说很重要,但是工作中 工作中一般都不会涉及的自己写算法之类的,只是面试,或者理解底层原理的时候需要 然后看一下人工智能需要哪些数学知识 这里需要微积分 线性代数 概率论 最优化的知识…

狂神说:面向对象(三)——多态

多态// 对象能执行什么方法&#xff0c;主要看对象左边的类型&#xff0c;和右边的没有关系多态&#xff1a;同一方法可以根据发送对象的不同而采用不同的行为方式父类&#xff1a;public class Person {public void run(){System.out.println("Person > run");}}…

跳跃游戏 (贪心/动态规划/dfs)

1.跳跃游戏简单介绍 跳跃游戏是一种典型的算法题目&#xff0c;经常是给定一数组arr[]&#xff0c;从数组的某一位置i出发&#xff0c;根据一定的跳跃规则&#xff0c;比如从i位置能跳arr[i]步&#xff0c;或者小于arr[i]步&#xff0c;或者固定步数&#xff0c;直到到达某一位…

Java 【数据结构OJ题十道】—— 二叉树篇2

文章目录一、二叉树前序遍历二、二叉树层序遍历三、按照之字形打印二叉树四、二叉树中和为某一值的路径(一)五、二叉搜索树与双向链表六、合并二叉树七、二叉树的镜像八、判断是否为二叉搜索树九、判断是否为完全二叉树十、判断是否为平衡二叉树总结提示&#xff1a;本人是正在…

TCP中RTT时延的理解

最近服务器环境部署了tcprtt网络时延监控&#xff0c;发现不同服务器不同节点之间的RTT时延表象非常奇怪&#xff0c;无法准确的判断服务器的网络情况。因此需要弄清楚什么是RTT&#xff0c;以及能否作为服务器网络性能的检测指标。 1、RTT是什么&#xff1f; TCP中的RTT指的是…

倾向得分匹配案例分析

一、倾向得分匹配法说明 倾向得分匹配模型是由Rosenbaum和Rubin在1983年提出的&#xff0c;首次运用在生物医药领域&#xff0c;后来被广泛运用在药物治疗、计量研究、政策实施评价等领域。倾向得分匹配模型主要用来解决非处理因素&#xff08;干扰因素&#xff09;的偏差。 …

为什么硬件性能监控很重要

当今的混合网络环境平衡了分布式网络和现代技术的实施。但它们并不缺少一个核心组件&#xff1a;服务器。保持网络正常运行时间归结为监控和管理导致网络停机的因素。极有可能导致性能异常的此类因素之一是硬件。使用硬件监控器监控网络硬件已成为一项关键需求。 硬件监视器是…

连接金蝶云星空,数据交互轻松搞定!丨三叠云

金蝶云星空 路径 拓展 >> 插件 功能简介 新增插件「金蝶云星空」。 用户可通过配置「金蝶云星空」插件&#xff0c;就可以实时获取「金蝶云星空」的数据&#xff0c;同时支持回填数据至金蝶系统内。 地图视图 路径 表单 >> 表单设计 功能简介 新增「地图视…

prometheus+cadvisor监控docker

官方解释 cAdvisor&#xff08;ContainerAdvisor&#xff09;为容器用户提供了对其运行容器的资源使用和性能特性的了解。它是一个正在运行的守护程序&#xff0c;用于收集、聚合、处理和导出有关正在运行的容器的信息。具体来说&#xff0c;它为每个容器保存资源隔离参数、历史…

活动目录(Active Directory)组策略管理工具

活动目录&#xff08;Active Directory&#xff09;是面向Windows Standard Server、Windows Enterprise Server以及 Windows Datacenter Server的目录服务。&#xff08;Active Directory不能运行在Windows Web Server上&#xff0c;但是可以通过它对运行Windows Web Server的…

虚拟数字人直播带货相比人工有哪些优势?

新经济时代的到来&#xff0c;彻底改变了传统的消费方式。虚拟数字人的出现&#xff0c;标志着新一波的消费升级到来。虚拟数字人直播带货&#xff0c;不仅降低了商家的带货成本&#xff0c;拉近了商家与消费者的距离&#xff0c;也给消费者带来全新的消费方式。 花西子虚拟形象…

华为OD机试模拟题 用 C++ 实现 - 删除最少字符(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 最多获得的短信条数(2023.Q1)) 文章目录 最近更新的博客使用说明删除最少字符题目输入输出描述示例一输入输出示例二输入输出Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率…

在Redis集群模式下使用pipeline进行批量操作

最近开始又接触到了Redis&#xff0c;之前在工作中使用Redis的时候&#xff0c;由于QPS不高&#xff0c;都是直接get/set搞定了。这次遇到的业务数据量比较大&#xff0c;更新也很频繁&#xff0c;Redis使用是集群模式&#xff0c;所以本文记录下捣鼓出来的如何在集群模式下使用…