【Redis】Redis 分布式锁误删问题

news2024/12/25 11:02:10

本文主要介绍 Redis 分布式锁误删问题的解决

场景一

1. 问题的产生情况一

因为业务阻塞,导致别人的锁被误删

在这里插入图片描述

2. 解决思路

获取锁的时候存入标识,释放锁的时候判断标识是否一致,一致可以释放锁,不一致不释放锁。

在这里插入图片描述

3. 解决代码

总体思路:

  • 获取锁的时候存入线程标识 , 用 UUID 表示 ()
  • 释放锁的时候,判断标识是否一致
public class SimpleRedisLock implements ILock{

    // 锁的 key 前缀
    private static final String KEY_PREFIX = "lock:";
    // 线程 id 的前缀
    private static final String ID_PREFIX = UUID.randomUUID().toString() + "-";
    // 锁的名字
    private String lockName;
    // 传入的 StringRedisTemplate
    private StringRedisTemplate stringRedisTemplate;


    public SimpleRedisLock(String lockName, StringRedisTemplate stringRedisTemplate) {
        this.lockName = lockName;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean tryLock(long timeoutSec) {
        // 获取当前线程标识
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        // 获取锁
        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX+lockName, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success); // 防止自动拆箱时候出现空指针问题
    }

    @Override
    public void unlock() {
        // 获取当前锁的线程 id
        String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + lockName);
        // 获取当前请求释放锁的线程 id
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        // 判断两者是否一致,一致给释放锁
        if(threadId.equals(id)){
            stringRedisTemplate.delete(KEY_PREFIX + lockName);
        }
    }
}



场景二

1. 问题的产生情况二

获取锁标识并判断一致后被阻塞导致误删问题

在这里插入图片描述

2. 解决思路

保证判断锁标识一致和删除锁这一操作的原子性

  1. Redis 事务:
  • 支持原子性,不支持一致性
  • 批处理操作,最终一致性 (基于乐观锁)
  1. Lua 脚本

Redis 提供了 Lua 脚本功能,在一个脚本中编写多条 Reids 命令,保证多条命令执行的原子性

https://www.runoob.com/lua/lua-tutorial.html

Redis 调用函数:

redis.call('命令名称', 'key', '其他参数')

Redis 执行脚本的命令:

在这里插入图片描述

eval "脚本语句"

eval 支持带参数脚本:

eval "return redis.call('set', KEYS[1], ARGV[1])" 1 name Rose

等价于 set name Rose

3. 解决代码

SpringBoot 整合 Lua 脚本实现锁的释放

释放锁的 Lua 脚本:

-- 获取锁中的线程标识
local id = redis.call('get, KEYS[1])
-- Redis中存入的线程标识和传入的参数一致可以删除
if(id == ARGV[1]) then
	return redis.call('del', KEYS[1])
end
return 0

Java 执行 Lua 脚本:

public class SimpleRedisLock implements ILock{

    // 锁的 key 前缀
    private static final String KEY_PREFIX = "lock:";
    // 线程 id 的前缀
    private static final String ID_PREFIX = UUID.randomUUID().toString() + "-";
    // 锁的名字
    private String lockName;
    // 传入的 StringRedisTemplate
    private StringRedisTemplate stringRedisTemplate;

    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;

    static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        // 设置脚本位置
        UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
        UNLOCK_SCRIPT.setResultType(Long.class);
    }


    public SimpleRedisLock(String lockName, StringRedisTemplate stringRedisTemplate) {
        this.lockName = lockName;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    @Override
    public boolean tryLock(long timeoutSec) {
        // 获取当前线程标识
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        // 获取锁
        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX+lockName, threadId, timeoutSec, TimeUnit.SECONDS);
        return Boolean.TRUE.equals(success); // 防止自动拆箱时候出现空指针问题
    }


    @Override
    public void unlock() {
        stringRedisTemplate.execute(UNLOCK_SCRIPT, Collections.singletonList(KEY_PREFIX + lockName), ID_PREFIX + Thread.currentThread().getId());
    }
}



总结

Redis 分布式锁实现思路:

  • 利用 setnx 获取锁,设置过期时间,保存 value 为线程标识
  • 释放锁时先判断线程标识是否一致,一致则删除锁

特性:

  • setnx 保证互斥性
  • 通过设置过期时间的方式,保证出现故障时锁仍然能释放,避免了死锁
  • 利用 Redis 集群保证高可用性和并发现


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

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

相关文章

今年,我只赚了一点点

大家好&#xff0c;我是 Jack。 之前一直有小伙伴问我&#xff0c;有没有免费的股票信息查询的 API 接口&#xff1f; 我看了一圈&#xff0c;很多免费的 API 接口都年久失修&#xff0c;失效了。 那好吧&#xff0c;咱自己写一个。 想要玩量化交易&#xff0c;第一步&…

015 | 乡村振兴背景下田园综合体建设对农户生计的影响 | 大学生创新训练项目申请书 | 极致技术工厂

研究目的 本项目旨在研究xx市xx区田园综合体建设对农户生计的影响。以农户生计资本和生计恢复力为切入点&#xff0c;以塘坝镇、崇龛镇、太安镇等六个地级镇田园综合体建设前后作对比&#xff0c;研究其田园综合体建设对农户生计的影响&#xff0c;以期找到乡村振兴背景下田园综…

前端岗位编写代码注意规范

&#x1f525;&#x1f525;&#x1f525;关注前端小王hs&#x1f525;&#x1f525;&#x1f525; &#x1f525;&#x1f525;&#x1f525;加底部微信&#x1f525;&#x1f525;&#x1f525; &#x1f525;&#x1f525;&#x1f525;学习指导/前端练手项目推荐/毕设选题/…

基于元胞自动机森林火灾模型及应用(Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

Allegro如何打印光绘层操作指导export模式

Allegro如何打印光绘层操作指导export模式 Allegro支持把光绘层面输出成pdf格式,下面介绍export模式如何输出,具体操作如下 打开光绘绘设置 确保光绘设置都是正确的 选择File-Export-PDF 弹出allegro PDF publisher,选择page setup Paper size选择A4,A4纸张 orientati…

SimKD

又搬来一个简单高效的知识蒸馏技术哦~~直接复用教师分类器还能显著减小性能差距的~ 在分类器的上一层通过特征对齐来训练学生模型&#xff0c;并直接复用教师分类器到学生模型中&#xff0c;再使用L2损失进行特征对齐。来自浙江大学的复用教师模型的方法哦~~ 浙大好厉害~~ 论…

2023年五面蚂蚁、三面拼多多、字节跳动最终拿offer入职拼多多

文章有点长&#xff0c;请耐心看完&#xff0c;绝对有收获&#xff01;不想听我BB直接进入面试分享&#xff1a; 准备过程 蚂蚁金服面试分享 拼多多面试分享 字节跳动面试分享 总结 说起来开始进行面试是年前倒数第二周&#xff0c;上午9点&#xff0c;我还在去公司的公交…

ERD Online 4.0.5 在线数据库建模、元数据管理(免费、私有部署)

4.0.5版本来袭❝ fix(erd): 增加数据库数据查询功能&#xff0c;支持多数据源切换查询&#xff0c;查看sql执行计划fix(erd): 数据查询功能&#xff0c;保留历史查询记录&#xff0c;格式化sql&#xff0c;多级树结构保存历史查询fix(erd): 依赖ERD加密手段&#xff0c;导出保留…

vdbench测试SSD快速入门

介绍 vdbench是一个I/O工作负载生成器&#xff0c;通常用于验证数据完整性和度量直接附加&#xff08;或网络连接&#xff09;存储性能。它可以运行在windows、linux环境&#xff0c;可用于测试文件系统或块设备基准性能。我们下面主要以块设备为介绍对象。 下载及安装 下载…

Linux 在过去几年发生的六种变化

随着时间的推移&#xff0c;Linux 桌面已经发生了变化&#xff0c;这种变化是逐渐发生的&#xff0c;因此这里汇总了过去十年中 Linux 桌面体验发生变化的一些具体方式。资深用户知道 Linux 桌面已经走过了漫长的道路。从前端应用程序设计到后端 Linux 组件&#xff0c;近年来发…

驱动无模块注入dll

文章目录实现效果三环无模块注入的方案反射型dll注入方式的改进零环无模块注入方案petoshellcode无模块注入流程实现代码Xenos注入方案研究IT_MMap注入IT_Thread注入IT_Apc注入火绒的注入思路实现效果 可以看到dll已经成功执行&#xff0c;但是在内存区域里面并没有我们的模块&…

《野球少年》:投捕搭档·棒球联盟

中文名 野球少年 原版名称 バッテリー 别 名 棒球伙伴、Battery 动画制作 ZERO-G 类 型 青春、运动、棒球 剧情简介 身为一名投手&#xff0c;原田巧是位拥有着拔群棒球才能的少年。在初中入学时移居的山间城镇新田市&#xff0c;巧与接住自己全力投球的捕手永仓豪相遇了。…

13 个你应该知道的 Webpack 优化技巧

Webpack 是目前前端开发最重要的构建工具。无论是自己的日常开发&#xff0c;还是准备面试&#xff0c;都应该掌握一些关于 Webpack 的优化技巧。 在这篇文章中&#xff0c;我将从三个方面分享一些我常用的技巧&#xff1a; 提高优化速度 压缩打包文件的大小 改善用户体验。…

[附源码]Python计算机毕业设计SSM基于框架的动漫设计(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

MarkDown语法浅析(基础语法)

本篇学习笔记简述MarkDown基础语法。掌握了“MarkDown基本语法简单HTML5标签”的综合运用&#xff0c;就可以把CSDN博文搞得美美哒✌ (本文获得CSDN质量评分【92】)【学习的细节是欢悦的历程】Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经…

SpringMVC笔记

文章目录一、SpringMVC简介1、什么是MVC2、什么是SpringMVC3、SpringMVC的特点二、HelloWorld1、开发环境2、创建maven工程a>添加web模块b>打包方式&#xff1a;warc>引入依赖3、配置web.xmla>默认配置方式b>扩展配置方式4、创建请求控制器5、创建springMVC的配…

Android开发中的服务发现技术

我们的日常开发中充满了InterfaceRegistry这种模式的代码&#xff0c;其中&#xff1a; Interface为定义的服务接口&#xff0c;可能是业务功能服务也可能是日志服务、数据解析服务、特定功能引擎等各种抽象层&#xff08;abstract layer&#xff09;&#xff1b;Registry为特…

线性表→顺序表→链表 逐个击破

一. 线性表 1. 前言 线性表&#xff0c;全名为线性存储结构。使用线性表存储数据的方式可以这样理解&#xff0c;即 “ 把所有(一对一逻辑关系的)数据用一根线儿串起来&#xff0c;再存储到物理空间中 ”。这根线有两种串联形式&#xff0c;如下图&#xff0c;即顺序存储(集中…

【收藏级】MySQL基本操作的所有内容(常看常新)

文章目录前言一、ER模型二、数据类型三、字段命名规范四、数据库创建与管理4.1、创建数据库4.2、删除数据库4.3、列出数据库4.4、备份数据库4.5、还原数据库4.6、使用某个数据库五、数据表创建与管理5.1、创建表、结构5.2、查看表结构5.3、查看数据表5.4、复制表结构5.5、复制表…

m基于PSO粒子群算法的重采样算法仿真,对比随机重采样,多项式重采样,分层重采样,系统重采样,残差重采样,MSV重采样

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 重采样的主要方法有随机重采样,多项式重采样,分层重采样,系统重采样,残差重采样,MSV重采样等。 a.随机采样是一种利用分层统计思想设计出来的&#xff0c;将空间均匀划分&#xff0c;粒子打点后…