【RedisLockRegistry】分布式锁

news2025/4/28 7:01:09

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作为锁的key
        Lock 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;
    }

    /**
     * 业务处理器接口(有返回值)
     */
    @FunctionalInterface
    public interface LockProcessor<T> {
        T process();
    }

    /**
     * 业务处理器接口(无返回值)
     */
    @FunctionalInterface
    public 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

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

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

相关文章

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.通用框架 总结 前言 端到端自动驾驶指的是系统接收来自摄像头雷达和激光雷达的原始传感…

CSRF请求伪造

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

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

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

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

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

力扣-160.相交链表

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

【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…

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

论文题目&#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:…

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…

小白学习java第15天:JDBC

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

大模型应用开发(PAFR)

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