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

【RedisLockRegistry】分布式锁

RedisLockRegistry分布式锁

介绍

RedisLockRegistry‌是Spring框架提供的一种分布式锁机制,它基于Redis来实现对共享资源的保护,防止多个进程同时对同一资源进行修改,从而避免数据不一致或其他问题‌

基本原理

RedisLockRegistry通过Redis的原子操作来实现分布式锁。其主要原理包括:

  • 独占性‌:同一时刻只能被一个客户端持有,确保互斥性。
  • 健壮性‌:通过设置锁的过期时间来防止死锁,确保锁能够在一定时间内自动释放,避免资源长时间被占用‌
  • 对称性‌:加锁和解锁必须由同一客户端执行,防止非法释放他人持有的锁‌
  • 高可用性‌:当部分节点故障时,不影响分布式锁服务的稳定性‌

核心特点

  • 互斥性:同一时刻只有一个客户端能持有锁

  • 可重入性:同一个客户端可以多次获取同一个锁

  • 超时机制:防止死锁,锁会自动释放

  • 高可用:基于 Redis,性能高且可靠

应用场景

  • 防止重复处理:如定时任务在集群环境下的执行控制

  • 资源争用:如库存扣减、秒杀系统

  • 关键业务流程:如支付订单处理

  • 分布式系统协调:如主节点选举

使用方法

1.添加依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-redis</artifactId>
</dependency>

2.配置分布式锁

@Configuration
public class RedisLockConfig {/*** 锁过期毫秒数*/private static final long EXPIRE_AFTER_MILLS = 600000L;@Bean(destroyMethod = "destroy")public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {return new RedisLockRegistry(redisConnectionFactory, "redis-lock", EXPIRE_AFTER_MILLS);}
}

默认有效时间是60秒,如果是默认的时间,则修改为

@Configuration
public class RedisLockConfig {/*** 锁过期毫秒数*/private static final long EXPIRE_AFTER_MILLS = 600000L;@Bean(destroyMethod = "destroy")public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {return new RedisLockRegistry(redisConnectionFactory, "redis-lock");}
}

3.基本使用示例

import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;@Service
public class OrderService {private final RedisLockRegistry redisLockRegistry;public OrderService(RedisLockRegistry redisLockRegistry) {this.redisLockRegistry = redisLockRegistry;}public void processOrder(String orderId) {// 获取锁对象,orderId作为锁的keyLock lock = redisLockRegistry.obtain(orderId);try {// 尝试获取锁,等待最多3秒,锁持有30秒(与配置一致)if (lock.tryLock(3, TimeUnit.SECONDS)) {try {// 获取锁成功,执行业务逻辑System.out.println("处理订单: " + orderId + ", 线程: " + Thread.currentThread().getName());// 模拟业务处理Thread.sleep(1000);} finally {// 释放锁lock.unlock();}} else {// 获取锁失败System.out.println("获取锁失败,订单: " + orderId);}} catch (InterruptedException e) {Thread.currentThread().interrupt();System.out.println("获取锁被中断");}}
}

4.高级用法-可重入锁

public void reentrantMethod(String resourceId) {Lock lock = redisLockRegistry.obtain(resourceId);try {if (lock.tryLock()) {try {System.out.println("外层方法获取锁");// 调用另一个也需要相同锁的方法nestedMethod(resourceId);} finally {lock.unlock();}}} catch (Exception e) {e.printStackTrace();}
}private void nestedMethod(String resourceId) {Lock lock = redisLockRegistry.obtain(resourceId);try {if (lock.tryLock()) {try {System.out.println("内层方法获取锁");// 业务逻辑} finally {lock.unlock();}}} catch (Exception e) {e.printStackTrace();}
}

5.定时任务分布式锁示例

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
public class ScheduledTasks {private final RedisLockRegistry redisLockRegistry;public ScheduledTasks(RedisLockRegistry redisLockRegistry) {this.redisLockRegistry = redisLockRegistry;}@Scheduled(cron = "0 */5 * * * ?")  // 每5分钟执行一次public void distributedCronJob() {Lock lock = redisLockRegistry.obtain("report-generation");try {if (lock.tryLock(10, TimeUnit.SECONDS)) {try {// 生成报表的业务逻辑System.out.println("开始生成报表...");Thread.sleep(5000);  // 模拟耗时操作System.out.println("报表生成完成");} finally {lock.unlock();}} else {System.out.println("其他节点正在生成报表,本节点跳过");}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}

RedisLockRegistry 分布式锁工具类

import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;/*** Redis分布式锁工具类*/
@Component
public class RedisDistributedLock {private final RedisLockRegistry redisLockRegistry;public RedisDistributedLock(RedisLockRegistry redisLockRegistry) {this.redisLockRegistry = redisLockRegistry;}/*** 尝试获取锁(立即返回)** @param lockKey 锁的key* @return 是否获取成功*/public boolean tryLock(String lockKey) {Lock lock = redisLockRegistry.obtain(lockKey);return lock.tryLock();}/*** 尝试获取锁(带超时时间)** @param lockKey 锁的key* @param timeout 超时时间* @param unit    时间单位* @return 是否获取成功*/public boolean tryLock(String lockKey, long timeout, TimeUnit unit) {Lock lock = redisLockRegistry.obtain(lockKey);try {return lock.tryLock(timeout, unit);} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;}}/*** 释放锁** @param lockKey 锁的key*/public void unlock(String lockKey) {Lock lock = redisLockRegistry.obtain(lockKey);lock.unlock();}/*** 执行带锁的业务逻辑(立即返回)** @param lockKey   锁的key* @param processor 业务处理器* @param <T>       返回值类型* @return 业务处理结果,如果获取锁失败返回null*/public <T> T executeWithLock(String lockKey, LockProcessor<T> processor) {if (tryLock(lockKey)) {try {return processor.process();} finally {unlock(lockKey);}}return null;}/*** 执行带锁的业务逻辑(带超时时间)** @param lockKey   锁的key* @param timeout   超时时间* @param unit      时间单位* @param processor 业务处理器* @param <T>       返回值类型* @return 业务处理结果,如果获取锁失败返回null*/public <T> T executeWithLock(String lockKey, long timeout, TimeUnit unit, LockProcessor<T> processor) {if (tryLock(lockKey, timeout, unit)) {try {return processor.process();} finally {unlock(lockKey);}}return null;}/*** 执行带锁的业务逻辑(无返回值,立即返回)** @param lockKey   锁的key* @param processor 无返回值的业务处理器* @return 是否成功获取锁并执行*/public boolean executeWithLock(String lockKey, VoidLockProcessor processor) {if (tryLock(lockKey)) {try {processor.process();return true;} finally {unlock(lockKey);}}return false;}/*** 执行带锁的业务逻辑(无返回值,带超时时间)** @param lockKey   锁的key* @param timeout   超时时间* @param unit      时间单位* @param processor 无返回值的业务处理器* @return 是否成功获取锁并执行*/public boolean executeWithLock(String lockKey, long timeout, TimeUnit unit, VoidLockProcessor processor) {if (tryLock(lockKey, timeout, unit)) {try {processor.process();return true;} finally {unlock(lockKey);}}return false;}/*** 业务处理器接口(有返回值)*/@FunctionalInterfacepublic interface LockProcessor<T> {T process();}/*** 业务处理器接口(无返回值)*/@FunctionalInterfacepublic interface VoidLockProcessor {void process();}
}

使用示例

1.基本用法
@Service
public class OrderService {private final RedisDistributedLock redisDistributedLock;public OrderService(RedisDistributedLock redisDistributedLock) {this.redisDistributedLock = redisDistributedLock;}public void processOrder(String orderId) {if (redisDistributedLock.tryLock(orderId, 3, TimeUnit.SECONDS)) {try {// 处理订单业务逻辑System.out.println("处理订单: " + orderId);} finally {redisDistributedLock.unlock(orderId);}} else {System.out.println("获取锁失败,订单: " + orderId);}}
}
2.使用函数式接口(推荐)
@Service
public class InventoryService {private final RedisDistributedLock redisDistributedLock;public InventoryService(RedisDistributedLock redisDistributedLock) {this.redisDistributedLock = redisDistributedLock;}public boolean deductStock(String productId, int quantity) {return redisDistributedLock.executeWithLock("stock:" + productId, 2, TimeUnit.SECONDS,() -> {// 在这里写扣减库存的业务逻辑System.out.println("扣减商品库存: " + productId + ", 数量: " + quantity);return true; // 返回业务处理结果}) != null;}
}
3.无返回值的使用方式
public void generateReport() {boolean executed = redisDistributedLock.executeWithLock("report-generation",5,TimeUnit.SECONDS,() -> {// 生成报表的业务逻辑System.out.println("开始生成报表...");Thread.sleep(3000);System.out.println("报表生成完成");});if (!executed) {System.out.println("其他节点正在生成报表,本次跳过");}
}

注意:

  • 确保锁的key具有唯一性,不同业务使用不同的key前缀

注意事项

  • 锁过期时间:设置合理的过期时间,太短可能导致业务未完成锁就释放,太长可能导致其他客户端等待过久

  • 异常处理:确保锁在finally块中释放,避免死锁

  • Redis可用性:Redis集群的高可用配置很重要,避免单点故障

  • 时钟同步:确保所有使用锁的服务器的系统时钟同步

锁粒度:根据业务需求选择合适的锁粒度,太粗影响并发,太细增加复杂度
参考博客
分布式锁之RedisLockRegistry

相关文章:

【RedisLockRegistry】分布式锁

RedisLockRegistry分布式锁 介绍 RedisLockRegistry‌是Spring框架提供的一种分布式锁机制&#xff0c;它基于Redis来实现对共享资源的保护&#xff0c;防止多个进程同时对同一资源进行修改&#xff0c;从而避免数据不一致或其他问题‌ 基本原理 RedisLockRegistry通过Redi…...

leetcode-排序

排序 面试题 01.01. 判定字符是否唯一 题目 实现一个算法&#xff0c;确定一个字符串 s 的所有字符是否全都不同。 示例 1&#xff1a; 输入: s "leetcode" 输出: false 示例 2&#xff1a; 输入: s "abc" 输出: true限制&#xff1a; 0 < len(s) &…...

AD相同网络的铜皮和导线连接不上

出现这样的情况是不是很烦恼&#xff0c;明明是相同的网络连接不上&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f; 直接修改铜皮属性&#xff08;选择所有相同这个选项&#xff09; 这样就可以连接上了...

keil修改字体无效,修改字体为“微软雅黑”方法

在网上下载了微软雅黑字体&#xff0c;微软雅黑参考下载链接 结果在Edit->Configuration中找不到这个字体 这个时候可以在keil的安装目录中找到UV4/global.prop文件 用记事本打开它进行编辑&#xff0c;把字体名字改成微软雅黑 重新打开keil就发现字体成功修改了。 这个…...

【网络编程】从零开始彻底了解网络编程(三)

本篇博客给大家带来的是网络编程的知识点. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅&#x1f680; 要开心要快乐顺便进步 TCP流…...

NVIDIA --- 端到端自动驾驶

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、传统驾驶模型二、NVIDIA的端到端驾驶模型1.基本模型2.自查讯向量3.通用框架 总结 前言 端到端自动驾驶指的是系统接收来自摄像头雷达和激光雷达的原始传感…...

关于在Springboot中设置时间格式问题

目录 1-设置全局时间格式1.Date类型的时间2.JDK8时间3.使Date类和JDK8时间类统统格式化时间 2-关于DateTimeFormat注解 1-设置全局时间格式 1.Date类型的时间 对于老项目来说&#xff0c;springboot中许多类使用的是Date类型的时间&#xff0c;没有用到LocalDateTime等JDK8时…...

CSRF请求伪造

该漏洞主要是关乎于用户&#xff0c;告诫用户不可乱点击链接&#xff0c;提升自我防范&#xff0c;才能不落入Hacker布置的陷阱&#xff01; 1. cookie与session 简单理解一下两者作用 1.1. &#x1f36a; Cookie&#xff1a;就像超市的会员卡 存储位置&#xff1a;你钱包里…...

(一)单机架构、应用数据分离架构、应用服务集群架构

文章目录 明确为什么要学习架构的演进单机架构什么是单机架构单机架构的模型单机架构的优缺点优点缺点 单机架构的技术案例 应用数据分离架构什么是应用数据分离架构架构模型应用数据分离架构的优缺点优点缺点 技术案例 应用服务集群架构什么是应用服务集群架构架构模型应用服务…...

Python数据分析案例72——基于股吧评论数据的情感分析和主题建模(LDA)

背景 好久没更新了&#xff0c;最近忙其他去了。最近股市波动太大&#xff0c;看了不少新闻的评论。抽空写了个股吧评论数据的LDA建模和情感分析&#xff0c;简单写到博客上来更新一下。 数据来源 上证指数(000001)股吧_上证指数怎么样_分析讨论社区— 数据来源上述网站的东…...

Git分支管理方案

成都众望智慧有限公司Git分支管理方案 采用 轻量级Git Flow 敏捷版本控制策略&#xff0c;在保证稳定性的同时提升开发效率。以下是优化后的方案&#xff1a; 1. 精简分支模型&#xff08;相比6-8人团队减少分支层级&#xff09; 分支类型作用生命周期devops生产环境代码&am…...

力扣-160.相交链表

题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返…...

c语言中float和double 类型的区别

在 C 语言里&#xff0c;float和double都用于表示浮点数&#xff0c;不过二者在多个方面存在差异&#xff0c;下面为你详细介绍&#xff1a; 1. 存储空间大小 在 C 语言中&#xff0c;数据类型所占用的存储空间大小通常与编译器和系统架构有关&#xff0c;但一般来说&#xf…...

【C++】特殊类的设计、单例模式以及Cpp类型转换

&#x1f4da; 博主的专栏 &#x1f427; Linux | &#x1f5a5;️ C | &#x1f4ca; 数据结构 | &#x1f4a1;C 算法 | &#x1f152; C 语言 | &#x1f310; 计算机网络 上篇文章&#xff1a; C 智能指针使用&#xff0c;以及shared_ptr编写 下篇文章&#xff…...

050_基于springboot的音乐网站

一、系统架构 前端&#xff1a;vue | element-ui | html | jquery | css | ajax 后端&#xff1a;springboot | mybatis 环境&#xff1a;jdk1.8 | mysql | maven | nodejs | idea 二、代码及数据 三、功能介绍 01. web端-注册 02. web端-登录 03. web…...

全局变量Msg.sender

msg.sender 在 Solidity 中&#xff0c;有一些全局变量可以被所有函数调用。 其中一个就是 msg.sender&#xff0c;它指的是当前调用者&#xff08;或智能合约&#xff09;的 address。 注意&#xff1a;在 Solidity 中&#xff0c;功能执行始终需要从外部调用者开始。 一个合…...

【论文阅读】平滑量化:对大型语言模型进行准确高效的训练后量化

论文题目&#xff1a;SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models 论文地址&#xff1a;[2211.10438] SmoothQuant: Accurate and Efficient Post-Training Quantization for Large Language Models 代码地址&#xff1a;http…...

【资料推荐】LVDS Owner’s Manual

一份年代有些久远的技术资料&#xff0c;但是内容全面且经典&#xff01; 本用户手册提供了很多有用的信息&#xff0c;首先简要概述了三种最常见的高速接口技术&#xff1a;LVDS&#xff08;包括B-LVDS和M-LVDS&#xff09;、CML和LVPECL&#xff0c;并对其相应的特性进行了分…...

ARM Cortex-M (STM32)如何调试HardFault

目录 步骤 1: 实现一个有效的 HardFault 处理程序 步骤 2: 复现 HardFault 并使用调试器分析 步骤 3: 解读故障信息 步骤 4: 定位并修复源代码 HardFault 是 ARM Cortex-M 处理器中的一种异常。当处理器遇到无法处理的错误&#xff0c;或者配置为处理特定类型错误&#xff…...

黑马 redis面试篇笔记

redis主从 version: "3.2"services:r1:image: rediscontainer_name: r1network_mode: "host"entrypoint: ["redis-server", "--port", "7001"]r2:image: rediscontainer_name: r2network_mode: "host"entrypoint:…...

Docker端口映射与容器间DNS发现:打通服务通信的任督二脉

Docker端口映射与容器间DNS发现&#xff1a;打通服务通信的任督二脉 一、端口映射深度解析1.1 端口映射核心机制映射规则语法&#xff1a; 1.2 高级映射技巧批量端口映射&#xff1a;查看端口绑定状态&#xff1a; 二、容器间服务发现机制2.1 自定义网络DNS体系DNS解析特性&…...

DBdriver使用taos数据库

首先创建连接 连接后比如数据库里有三个库 选择其中的hypon 选中localhost&#xff0c;右键sql编辑器&#xff0c;打开sql控制台 就插入了一条数据...

观成科技:摩诃草组织Spyder下载器流量特征分析

一、概述 自2023年以来&#xff0c;摩诃草组织频繁使用Spyder下载器下载远控木马&#xff0c;例如Remcos。观成安全研究团队对近几年的Spyder样本进行了深入研究&#xff0c;发现不同版本的样本在数据加密、流量模式等方面存在差异。基于此&#xff0c;我们对多个版本样本的通…...

AIGC实战之如何构建出更好的大模型RAG系统

一、RAG 系统核心架构解析 1. 检索模块深度优化 1.1 混合检索技术实现 技术原理&#xff1a;结合稀疏检索&#xff08;BM25&#xff09;与密集检索&#xff08;DPR&#xff09;&#xff0c;通过动态权重分配提升检索精度。例如&#xff0c;在医疗领域&#xff0c;BM25 负责精…...

C++入门小馆: 深入了解STLlist

嘿&#xff0c;各位技术潮人&#xff01;好久不见甚是想念。生活就像一场奇妙冒险&#xff0c;而编程就是那把超酷的万能钥匙。此刻&#xff0c;阳光洒在键盘上&#xff0c;灵感在指尖跳跃&#xff0c;让我们抛开一切束缚&#xff0c;给平淡日子加点料&#xff0c;注入满满的pa…...

【Git】Fork和并请求

当你在 GitHub 或其他代码托管平台上 Fork 了一个项目后&#xff0c;你可以基于你的 Fork 进行开发&#xff0c;并通过 Pull Request&#xff08;PR&#xff09; 的方式将你的更改提交给原始项目&#xff08;也称为上游仓库&#xff09;。以下是完整的流程和步骤&#xff1a; 1…...

小白学习java第15天:JDBC

1.数据库驱动 想一下我们之前是怎么操作数据库&#xff0c;是不是使用SQL语句对其mysql数据库管理系统&#xff0c;然后管理系统在进行数据库&#xff08;硬盘文件里面的&#xff09;进行操作。那么我现在想使用应用程序对其数据库进行操作&#xff0c;应该怎么办呢&#xff1…...

大模型应用开发(PAFR)

Prompt问答 特征:利用大模型推理能力完成应用的核心功能 应用场景&#xff1a; 文本摘要分析 舆情分析 坐席检查 AI对话 AgentFunction Calling 特征&#xff1a;将应用端业务能力与AI大模型推理能力结合&#xff0c;简化复杂业务功能开发 应用场景: 旅行指南 数据…...

Go 剥离 HTML 标签的三把「瑞士军刀」——从正则到 Bluemonday

1 为什么要「剥皮」&#xff1f; 安全&#xff1a;去掉潜在的 <script onload…> 等恶意标签&#xff0c;防止存储型 XSS。可读性&#xff1a;日志、消息队列、搜索索引里往往只需要纯文本。一致性&#xff1a;不同富文本编辑器生成的 HTML 五花八门&#xff0c;统一成「…...

U-Mail邮件加速服务:全球链路加速,安全稳定收发

由于跨国网络拥堵、带宽不稳定等因素&#xff0c;导致海外用户在使用企业邮箱收发邮件时&#xff0c;经常出现邮件收发不畅的问题。针对这种情况&#xff0c;U-Mail正式推出了邮件加速服务&#xff0c;U-Mail邮件加速服务依托全球优质加速链路和转发集群服务器&#xff0c;为海…...