Redis 分布式锁

article/2025/10/13 5:11:06

文章目录

    • 一、分布式锁概念
    • 二、使用setnx实现锁
    • 三、编写代码测试分布式锁
      • 1. 使用Java代码测试分布式锁
      • 2. 优化之设置锁的过期时间
    • 四、优化之给lock设置UUID防误删
    • 五、使用LUA脚本保证删除的原子性

一、分布式锁概念

随着业务发展的需要,原单机部署的系统被演化成分布式集群系统后,由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的 Java API 并不能提供分布式锁的能力。为了解决这个问题就需要一种跨 JVM 的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题!

说得通俗些,集群中上了锁后,无论当前操作在哪台机器,所有的机器都会识别并且等待,锁释放后其他操作才能进行,这就是分布式锁,对所有集群里都有效

分布式锁主流的实现方案:

  1. 基于数据库实现分布式锁
  2. 基于缓存(Redis 等)
  3. 基于 Zookeeper

每一种分布式锁解决方案都有各自的优缺点,其中redis性能最高zookeeper可靠性最高

二、使用setnx实现锁

set stu:1:info “OK” nx px 10000
  • EX second :设置键的过期时间为 second 秒,,SET key value EX second 效果等同于 SETEX key second value

  • PX millisecond :设置键的过期时间为 millisecond 毫秒,SET key value PX millisecond 效果等同于 PSETEX key millisecond value

  • NX :只在键不存在时,才对键进行设置操作,SET key value NX 效果等同于 SETNX key value

  • XX :只在键已经存在时,才对键进行设置操作

在这里插入图片描述

  • 多个客户端同时获取锁(setnx)
  • 获取成功,执行业务逻辑(从 db 获取数据,放入缓存),执行完成释放锁(del)
  • 获取失败的客户端则等待重试

在这里插入图片描述

用setnx和del添加以及释放锁

在这里插入图片描述
一般地,我们需要给锁设置过期时间防止锁被长期占用

在这里插入图片描述

这里有个问题:加锁和设置过期时间是两个操作,而不是同时进行操作的,如果上锁后发生异常情况,就无法设置过期时间了。我们可以上锁的同时设置过期时间

在这里插入图片描述

三、编写代码测试分布式锁

1. 使用Java代码测试分布式锁

首先在redis中设置num的值为0,编写Java代码进行测试

下方代码做的就是:获取到锁则num++,并释放锁;没获取到则0.1秒后重新获取
在这里插入图片描述

重启,服务集群,通过网关压力测试:ab -n 5000 -c 100 http://192.168.140.1:8080/test/testLock


查看 redis 中 num 的值

在这里插入图片描述

问题: setnx 刚好获取到锁,业务逻辑出现异常,导致锁无法释放
解决: 设置过期时间,自动释放锁

2. 优化之设置锁的过期时间

设置过期时间有两种方式:

  • 首先想到通过 expire 设置过期时间(缺乏原子性:如果在 setnx 和 expire 之 间出现异常,锁也无法释放)
  • 在 set 的同时指定过期时间(推荐)

在这里插入图片描述
代码中设置过期时间:

在这里插入图片描述

问题: 可能会释放其他服务器的锁

如果业务逻辑的执行时间是 7s,执行流程如下:

  • index1 业务逻辑没执行完,3 秒后锁被自动释放
  • index2 获取到锁,执行业务逻辑,3 秒后锁被自动释放
  • index3 获取到锁,执行业务逻辑
  • index1 业务逻辑执行完成,开始调用 del 释放锁,这时释放的是 index3 的锁, 导致 index3 的业务只执行 1s 就被别人释放。
    最终等于没锁的情况

在这里插入图片描述

a在操作时卡顿了,导致锁超时后自动释放;释放后,b抢到锁进行操作;此时a操作完成,手动释放锁,这就把b的锁给释放了,b再释放锁则会报错

解决: setnx 获取锁时,设置一个指定的唯一值(例如:uuid);释放前获取这 个值,判断是否自己的锁

四、优化之给lock设置UUID防误删

在这里插入图片描述

在这里插入图片描述

五、使用LUA脚本保证删除的原子性

使用lock的uuid可以一定程度上缓解线程释放其他锁,但并不能完全解决这种情况。因为比较uuid和删除lock并不是原子性的

在这里插入图片描述
问题: a比较uuid通过后,锁到期了自动释放,b重新加锁,a此时会手动释放b的锁,这还是出现问题

解决: 使用LUA 脚本保证删除的原子性

LUA脚本:

  • 将复杂的或者多步的 redis 操作,写为一个脚本,一次提交给 redis 执行,减少反复连接 redis 的次数,提升性能

  • LUA 脚本是类似 redis 事务,有一定的原子性,不会被其他命令插队,可以完成一些redis 事务性的

@GetMapping("testLockLua")
public void testLockLua() {//1 声明一个 uuid ,将做为一个 value 放入我们的 key 所对应的值中String uuid = UUID.randomUUID().toString();//2 定义一个锁:lua 脚本可以使用同一把锁,来实现删除!String skuId = "25"; // 访问 skuId 为 25 号的商品 100008348542String locKey = "lock:" + skuId; // 锁住的是每个商品的数据// 3 获取锁Boolean lock = redisTemplate.opsForValue().setIfAbsent(locKey, uuid, 3, TimeUnit.SECONDS);// 第一种: lock 与过期时间中间不写任何的代码。// redisTemplate.expire("lock",10, TimeUnit.SECONDS);//设置过期时间// 如果 trueif (lock) {// 执行的业务逻辑开始// 获取缓存中的 num 数据Object value = redisTemplate.opsForValue().get("num");// 如果是空直接返回if (StringUtils.isEmpty(value)) {return;}// 不是空 如果说在这出现了异常! 那么 delete 就删除失败! 也就是说锁永远存在!int num = Integer.parseInt(value + "");// 使 num 每次+1 放入缓存redisTemplate.opsForValue().set("num", String.valueOf(++num));/*使用 lua 脚本来锁*/// 定义 lua 脚本:将判断和删除操作同时进行String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";// 使用 redis 执行 lua 执行DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();redisScript.setScriptText(script);// 设置一下返回值类型 为 Long// 因为删除判断的时候,返回的 0,给其封装为数据类型。如果不封装那么默认返回 String 类型,// 那么返回字符串与 0 会有发生错误。redisScript.setResultType(Long.class);// 第一个是执行的 script 脚本 ,第二个需要判断的 key,第三个就是 key 所对应的值。redisTemplate.execute(redisScript, Arrays.asList(locKey), uuid);} else {// 其他线程等待try {// 睡眠Thread.sleep(1000);// 睡醒了之后,调用方法。testLockLua();} catch (InterruptedException e) {e.printStackTrace();}}
}

在这里插入图片描述

在这里插入图片描述

为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

  • 互斥性;在任意时刻,只有一个客户端能持有锁
  • 不会发生死锁;即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁(设置lock的过期时间)
  • 解铃还须系铃人;加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了(使用LUA脚本和uuid)
  • 加锁和解锁必须具有原子性(使用LUA脚本)

http://chatgpt.dhexx.cn/article/gl9nS8J0.shtml

相关文章

关于分布式锁

先别说了别的&#xff0c;先来一个总结。 synchronized 单机版可以&#xff0c;但是上了分布式就不行了。 nginx 分布式服务单机锁就不行 取消单机锁&#xff0c;上redis分布式锁setnx 注意的问题&#xff1a; 如果只加了锁&#xff0c;没有释放锁&#xff0c;出现异常的话…

Redisson分布式锁详解

概述 setnx分布式锁的问题 重入问题 重入问题是指获得锁的线程可以再次进入到相同的锁的代码块中&#xff0c;可重入锁的意义在于防止死锁&#xff0c;比如HashTable这样的代码中&#xff0c;它的方法都是使用synchronized修饰的&#xff0c;假如它在一个方法内&#xff0c;…

redission实现分布式锁

在开始提到Redis分布式锁之前&#xff0c;先说一下redis中的两个命令。 SETNX key valuesetnx 是SET if Not eXists(如果不存在&#xff0c;则 SET)的简写。 用法如图&#xff0c;如果不存在set成功返回int的1&#xff0c;这个key存在了返回0。 SETEX key seconds value上面…

Java分布式锁

文章目录 1.什么是锁&#xff1f;2.什么是分布式&#xff1f;分布式场景 3.什么是分布式锁&#xff1f;4.我们应该怎么设计分布式锁&#xff1f;5.基于数据库的分布锁5.1 基于表主键唯一做分布式锁5.2 基于表字段版本号做分布式锁 6.基于 Redis 做分布式锁6.1 基于 REDIS 的 SE…

Redis分布式锁

概述 日常开发中&#xff0c;秒杀下单、抢红包等等业务场景&#xff0c;都需要用到分布式锁。而Redis非常适合作为分布式锁使用。本文将分七个方案展开&#xff0c;跟大家探讨Redis分布式锁的正确使用方式。如果有不正确的地方&#xff0c;欢迎大家指出哈&#xff0c;一起学习一…

Zookeeper分布式锁

实现一把分布式锁通常有很多方法&#xff0c;比较常见的有 Redis 和 Zookeeper。 Redis分布式锁可参考之前的文章&#xff1a; Redisson 分布式锁原理分析&#xff1a;https://blog.csdn.net/qq_42402854/article/details/123342331 Zookeeper能实现分布式锁&#xff0c;是因…

分布式锁

分布式锁实践 在不同进程需要互斥地访问共享资源时&#xff0c;分布式锁是一种非常有用的技术手段。有很多三方库和文章描述如何用Redis实现一个分布式锁管理器&#xff0c;但是这些库实现的方式差别很大&#xff0c;而且很多简单的实现其实只需采用稍微增加一点复杂的设计就可…

分布式系列之分布式锁几种实现机制

在分布式系统中&#xff0c;分布式锁用来解决分布式系统中多线程、多进程在不同机器上共享资源访问的问题。本文简要介绍分布式锁的四种实现机制&#xff0c;包括数据库、Redis缓存、Zookeeper和Etcd&#xff0c;以加深了解。 1、分布式锁介绍 在单体应用中&#xff0c;通过锁…

三种分布式锁

----------本文为学习记录如有错误帮忙指正 一、什么是分布式锁&#xff1f; 在单机系统下&#xff0c;如果多个线程同时访问一个变量或者代码片段就会产生多线程问题。&#xff08;被访问的变量或者代码片段被称之为临界区域&#xff09;这时我们就需要让所有线程按顺序一个一…

Redis实现分布式锁

目录 一、前言 为什么需要分布式锁&#xff1f; 二、基于redis实现分布式锁 为什么redis可以实现分布式锁&#xff1f; 如何实现&#xff1f; 锁的获取 锁的释放 三、如何避免死锁&#xff1f;锁的过期时间如何设置&#xff1f; 避免死锁 锁过期处理 释放其他服务的锁…

什么是分布式锁?几种分布式锁分别是怎么实现的?

一、什么是分布式锁&#xff1a; 1、什么是分布式锁&#xff1a; 分布式锁&#xff0c;即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题&#xff0c;而分布式锁&#xff0c;就是解决了分布式系统中控制共享资源访问的问题。与单体应用不同的是&am…

软件需求最佳实践笔记(一)

1.软件需求最佳实践笔记 | 需求框架 前言&#xff1a;SERU是一套系统全面的需求方法论&#xff0c;可指导我们日常的软件需求工作。曾参加过徐峰老师软件需求最佳实践课程的培训&#xff0c;收益颇多&#xff0c;现通过笔记形式整理出来&#xff0c;以期与具有同样需求的读者共…

声音信号基音提取算法基频和谐波处理分析

1、内容简介 略 293-可以交流、咨询、答疑 2、内容说明 略 一、 实验原理&#xff1a; 傅里叶变换建立了信号频谱的概念。所谓傅里叶分析即分析信号的频谱(频率构成)、频带宽度等。要想合成出一段音乐,就要了解该段音乐的基波频率、谐波构成等。因此,必须采用傅里叶变换这…

软件工程—需求分析阶段

第一步、需求获取 为了保证能全面地获取信息&#xff0c;以更好地服务于产品设计和迭代&#xff0c;产品经理必须利用内部外部等多种渠道来获取用户需求。并且因渠道差异&#xff0c;产品经理所采取的方式与方法也相应会有所差异&#xff0c;所以产品经理还必须根据不同的渠道…

作业1.1利用Audacity软件分析音频

文章目录 前言实验内容实验步骤实验结果结果分析总结 前言 Audacity软件分析其余格式的音频时需要安装FFmpeg库&#xff0c;所以我们下载一个格式转换软件将音频转为MP3格式进行处理。语音信号具有短时平稳性&#xff0c;即在一个短时间范围内&#xff08;10-30ms&#xff09;…

C++ OBS源码分析与屏幕录制软件开发视频教程

本课程主要讲解OBS源码的编译&#xff0c;OBS功能实现&#xff0c;初始化&#xff0c;显示器录制&#xff0c;窗口的实现录制&#xff0c;以及录制模块源码详细分析&#xff0c;最后基于OBS源码开发了一个录制软件&#xff0c;界面如下&#xff1a; 主要有如下功能 &#xff0…

酒店管理系统-需求分析报告

目录 1.引言 1.1编制的目的 1.2术语定义 1.3参考资料 1.4相关文档 2.概述 2.1项目的描述 2.2项目的功能 2.3用户特点 3.具体需求 3.1业务需求 3.1.1主要业务 3.1.2未来增长预测 3.2用户需求 3.3应用需求 3.3.1系统功能 3.3.2主要应用及使用方式 3.4网络基本结构…

基于matlab的声波分析研究,基于MATLAB的声音信号分析与处理(共13页)

设计了一套信号采集与处理系统&#xff0c;建立了傅立叶变换算法模型&#xff0c;可获得其频谱图进行频谱分析&#xff0c;建立滤波器的设计算法模型设计了一个声音滤波器&#xff0c;建立滤波算法模型可对声音信号进行滤波。本套系统的算法建立都是基于MATLAB软件&#xff0c;…

分析评估和定位声音质量

/** * author wangdaopo * email 3168270295qq.com */ 影响音频质量和稳定性的因素 音质好坏的评价&#xff0c;响度、音高、音色&#xff0c; 测试&#xff0c;你的语音引擎是基本可用的&#xff0c;客观评测软件是RMAA&#xff08;RightMark Audio Analyzer&#xff1b;比…

声学计算机软件,常用声学仿真软件汇总

声学仿真软件根据计算原理不同大致分为以下几类&#xff1a; 一、电力声类比法 将振动系统和声学系统转化为等效电路&#xff0c;是一种0维的参数化建模方法&#xff1b; 优点&#xff1a;计算速度快&#xff1b; 缺点&#xff1a;无法预测高频响应以及复杂声波叠加&#xff1b…