Redisson 分布式锁的使用详解

一、分布式锁的概述

1.1 分布式锁的背景

在单机系统中,Java 提供了 synchronizedLock 等锁机制来确保并发情况下的线程安全。然而,在分布式系统中,多个服务实例运行在不同的物理或虚拟机上,无法直接使用这些本地的锁机制来控制对共享资源的访问。

为了在分布式环境中控制对共享资源的访问,就需要引入一种能够跨多台服务器实例的锁机制,这就是分布式锁。Redis 作为一种高性能的内存数据库,提供了基于 Redis 的分布式锁实现。Redisson 基于 Redis 对分布式锁进行了封装,提供了简洁易用的 API 来管理分布式锁。

1.2 分布式锁的特性

一个完整的分布式锁需要具备以下几个特性:

  1. 互斥性:同一时刻只能有一个客户端能获得锁,其他客户端必须等待锁被释放后才能继续操作。
  2. 容错性:如果持有锁的客户端因为网络或系统故障而没有主动释放锁,锁应当能够被安全地释放。
  3. 高可用性:锁的请求和释放过程应该是高效的,且在 Redis 节点出现故障时,系统依然能够正常工作。

二、Redisson 分布式锁的原理

Redisson 分布式锁基于 Redis 的 SETNX (set if not exists) 命令实现。基本的原理如下:

  1. 加锁:客户端请求加锁时,会向 Redis 发送一个 SETNX 命令,如果返回值为 1,表示加锁成功。如果返回 0,则说明锁已经被其他客户端持有,当前客户端需要等待。
  2. 自动过期:为了防止客户端因故障而无法释放锁,锁的持有是有过期时间的。通过 Redis 的 EXPIRE 命令,锁在加锁时同时设置过期时间,到期后锁将自动释放。
  3. 解锁:锁持有者执行完任务后,主动向 Redis 发送命令删除锁的 key,从而释放锁,其他等待的客户端才能继续加锁。

Redisson 通过封装 Redis 的这些基础命令,提供了 RLock 接口来方便地实现分布式锁。

三、Redisson 分布式锁的使用

3.1 引入 Redisson 依赖

在项目中使用 Redisson,首先需要在 pom.xml 文件中引入 Redisson 的依赖:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.16.3</version>
</dependency>

3.2 Redisson 配置

为了使用 Redisson 分布式锁,首先需要配置 Redisson 客户端。Redisson 支持单节点模式、主从模式、集群模式和哨兵模式等多种 Redis 部署模式。这里我们以单节点模式为例:

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class RedissonConfig {

    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        return Redisson.create(config);
    }
}

3.3 使用分布式锁

在配置好 Redisson 客户端后,可以通过 RedissonClient 来获取 RLock 对象,进行分布式加锁和解锁操作。下面是一个简单的使用示例:

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;

public class DistributedLockExample {

    private final RedissonClient redissonClient;

    public DistributedLockExample(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public void executeWithLock() {
        // 获取锁对象
        RLock lock = redissonClient.getLock("myLock");

        try {
            // 加锁
            lock.lock();
            System.out.println("获得锁,执行临界区代码");

            // 模拟业务逻辑
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 解锁
            lock.unlock();
            System.out.println("释放锁");
        }
    }
}

在上面的示例中,我们通过 redissonClient.getLock("myLock") 获取一个名为 myLock 的锁对象,并在执行完任务后通过 lock.unlock() 主动释放锁。

3.4 设置锁的超时时间

为了防止因异常情况导致锁无法释放,Redisson 提供了设置锁超时时间的功能。在获取锁时,可以通过 lock.lock(long leaseTime, TimeUnit unit) 方法指定锁的持有时间。例如,下面的代码将设置锁的持有时间为 10 秒:

public void executeWithTimeoutLock() {
    RLock lock = redissonClient.getLock("myLock");

    try {
        // 加锁并设置超时时间为10秒
        lock.lock(10, TimeUnit.SECONDS);
        System.out.println("获得锁,执行临界区代码");

        // 模拟业务逻辑
        Thread.sleep(5000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        // 无需主动解锁,锁会在10秒后自动释放
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
        System.out.println("释放锁");
    }
}

四、Redisson 分布式锁的应用场景

4.1 控制并发访问

在电商、抢购等高并发场景中,多个用户同时发起对同一商品的抢购请求,此时需要使用分布式锁来控制对库存资源的并发访问,防止超卖或重复购买。

public void purchase(String productId) {
    RLock lock = redissonClient.getLock("product:" + productId);

    try {
        // 尝试获取锁
        if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
            // 执行购买逻辑
            System.out.println("购买成功");
        } else {
            System.out.println("获取锁失败,购买失败");
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

4.2 分布式任务调度

在分布式系统中,任务调度器往往有多个实例在同时运行。如果多个实例同时执行相同的任务,可能会导致数据重复处理或任务冲突。此时可以通过 Redisson 分布式锁确保同一时刻只有一个实例能够执行任务。

public void scheduleTask() {
    RLock lock = redissonClient.getLock("taskLock");

    try {
        if (lock.tryLock(1, TimeUnit.MINUTES)) {
            System.out.println("获得锁,执行调度任务");
            // 执行定时任务
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        if (lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

4.3 分布式事务

在分布式系统中,为了确保多个数据库或服务之间的数据一致性,可以使用 Redisson 分布式锁来控制对共享资源的访问,从而实现分布式事务的控制。

五、Redisson 分布式锁的优缺点

5.1 优点

  1. 实现简单:Redisson 提供了简洁的 API,开发者可以轻松地实现分布式锁功能。
  2. 丰富的分布式工具:除了分布式锁,Redisson 还提供了其他分布式工具,如分布式集合、队列、布隆过滤器等,适用于多种场景。
  3. 自动续期:Redisson 提供了自动续期功能,防止因任务执行时间过长导致锁意外释放。

5.2 缺点

  1. 依赖 Redis:Redisson 是基于 Redis 实现的分布式锁,依赖于 Redis 的可用性和性能。
  2. **锁的粒度

问题**:在使用分布式锁时,需要合理设计锁的粒度。锁粒度过大会导致性能瓶颈,锁粒度过小则可能导致竞争加剧。

六、总结

Redisson 作为 Redis 的高级客户端,提供了简单易用的分布式锁实现,能够很好地解决分布式系统中的并发控制问题。通过 Redisson,开发者可以快速实现分布式锁,并应用到电商、任务调度、分布式事务等场景中。

在使用 Redisson 分布式锁时,除了基本的加锁和解锁操作外,还需要合理设计锁的粒度,确保锁的可用性和系统性能的平衡。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux重要文件

/etc/sysconfig/network-scripts/ifcfg-eth1 网卡重启 /etc/init.d/network restart ifup ethname & ifdown ethname /etc/resolv.conf 设置Linux本地的客户端DNS的配置文件 linux客户端DNS可以在网卡配置文件(/etc/sysconfig/network/ifcfg-eth0 DNS2)里配置 也可以在/et…

Java_Day04学习

类继承实例 package com.dx.test03; public class extendsTest {public static void main(String args[]) {// 实例化一个Cat对象&#xff0c;设置属性name和age&#xff0c;调用voice()和eat()方法&#xff0c;再打印出名字和年龄信息/********* begin *********/Cat cat ne…

Pandas -----------------------基础知识(一)

目录 Series对象 属性和方法 布尔值列表获取Series对象中部分数据 运算 DateFrame对象 常用属性 常见方法 运算 总结 Series对象 是DataFrame的列对象或者行对象 生成Series对象生成索引使用元组创建Series对象使用字典创建Series对象 通过Pandas创建对象 自定义索引 …

面试官问:你最自豪的成就是什么?

当面试官问你最自豪的成就是什么&#xff0c;我们首先分析面试官为什么这么问&#xff0c;他想通过这问题得到什么信息&#xff1f; 你最自豪的成就是什么&#xff1f; 其实反应了一个人的职业驱动力&#xff0c;比如我们常说的&#xff1a;上进心&#xff0c;主动积极性&…

【机器学习-监督学习】朴素贝叶斯

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈Python机器学习 ⌋ ⌋ ⌋ 机器学习是一门人工智能的分支学科&#xff0c;通过算法和模型让计算机从数据中学习&#xff0c;进行模型训练和优化&#xff0c;做出预测、分类和决策支持。Python成为机器学习的首选语言&#xff0c;…

【小沐学GIS】基于Openstreetmap创建Sionna RT场景(Python)

文章目录 1、简介1.1 blender 2、下载和安装2.1 Python2.2 jupyter 3、运行结语 1、简介 1.1 blender https://www.blender.org/ Blender 是一款免费开源的3D创作套件。 使用 Blender&#xff0c;您可以创建3D可视化效果&#xff0c;例如静态图像、3D动画、VFX&#xff08;…

【UE5】将2D切片图渲染为体积纹理,最终实现使用RT实时绘制体积纹理【第一篇-原理】

如果想直接制作&#xff0c;请看【第二篇】内容 这次做一个这样的东西&#xff0c;通过在2DRT上实时绘制&#xff0c;生成动态的体积纹理&#xff0c;也就是可以runtime的VDB 设想的文章流程: 对原理进行学习制作体积渲染制作实时绘制 第一篇&#xff08;本篇&#xff09;是对“…

【Rust练习】16.模式

文章题目来自&#xff1a;https://practice-zh.course.rs/pattern-match/patterns.html 1 &#x1f31f;&#x1f31f; 使用 | 可以匹配多个值, 而使用 … 可以匹配一个闭区间的数值序列 fn main() {} fn match_number(n: i32) {match n {// 匹配一个单独的值1 > println!(…

【赵渝强老师】K8s中的Deployment控制器

K8s的Deployment将Pod部署成无状态的应用程序&#xff0c;它只关心Pod的数量、Pod更新方式、使用的镜像和资源限制等。由于是无状态的管理方式&#xff0c;因此Deployment中没有角色和顺序的概念&#xff0c;换句话说&#xff1a;Deployment中没有状态。   通过使用Deploymen…

【远程调用PythonAPI-flask】

文章目录 前言一、Pycharm创建flask项目1.创建虚拟环境2.创建flask项目 二、远程调用PythonAPI——SpringBoot项目集成1.修改PyCharm的host配置2.防火墙设置3.SpringBoot远程调用PythonAPI 前言 解决Pycharm运行Flask指定ip、端口更改无效的问题 首先先创建一个新的flask项目&…

C语言 | Leetcode C语言题解之第415题字符串相加

题目&#xff1a; 题解&#xff1a; char* addStrings(char* num1, char* num2) {int i strlen(num1) - 1, j strlen(num2) - 1, add 0;char* ans (char*)malloc(sizeof(char) * (fmax(i, j) 3));int len 0;while (i > 0 || j > 0 || add ! 0) {int x i > 0 ?…

Games101学习 - 着色

本文主要讲述Games101中的着色部分。 文中将使用UE的UTexture2D接口&#xff0c;若不了解可以看这篇&#xff1a; https://blog.csdn.net/grayrail/article/details/142165442 1.面积比计算三角形坐标 通过三角形面积比可以得到三角形的坐标alpha、beta、gamma从而进行插值&a…

ChatGPT 4o 使用指南 (9月更新)

首先基础知识还是要介绍得~ 一、模型知识&#xff1a; GPT-4o&#xff1a;最新的版本模型&#xff0c;支持视觉等多模态&#xff0c;OpenAI 文档中已经更新了 GPT-4o 的介绍&#xff1a;128k 上下文&#xff0c;训练截止 2023 年 10 月&#xff08;作为对比&#xff0c;GPT-4…

【Linux笔记】虚拟机内Linux内容复制到宿主机的Window文件夹(文件)中

一、共享文件夹 I、Windows宿主机上创建一个文件夹 目录&#xff1a;D:\Centos_iso\shared_files II、在VMware中设置共享文件夹 1、打开VMware Workstation 2、选择需要设置的Linux虚拟机&#xff0c;点击“编辑虚拟机设置”。 3、在“选项”标签页中&#xff0c;选择“共…

Vue学习记录之三(ref全家桶)

ref、reactive是在 setup() 声明组件内部状态用的&#xff0c; 这些变量通常都要 return 出去&#xff0c;除了供 < template > 或渲染函数渲染视图&#xff0c;也可以作为 props 或 emit 参数 在组件间传递。它们的值变更可触发页面渲染。 ref &#xff1a;是一个函数&…

前端组件库

vant2现在的地址 Vant 2 - Mobile UI Components built on Vue

【学习笔记】手写Tomcat 四

目录 一、Read 方法返回 -1 的问题 二、JDBC 优化 1. 创建配置文件 2. 创建工具类 3. 简化 JDBC 的步骤 三、修改密码 优化返回数据 创建修改密码的页面 注意 测试 四、优化响应动态资源 1. 创建 LoginServlet 类 2. 把登录功能的代码放到 LoginServlet 类 3. 创…

【工具变量】科技金融试点城市DID数据集(2000-2023年)

时间跨度&#xff1a;2000-2023年数据范围&#xff1a;286个地级市包含指标&#xff1a; year city treat post DID&#xff08;treat*post&#xff09; 样例数据&#xff1a; 包含内容&#xff1a; 全部内容下载链接&#xff1a; 参考文献-pdf格式&#xff1a;https://…

Rust GUI框架 tauri V2 项目创建

文章目录 Tauri 2.0创建应用文档移动应用开发 Android 前置要求移动应用开发 iOS 前置要求参考资料 Tauri 2.0 Tauri 是一个构建适用于所有主流桌面和移动平台的轻快二进制文件的框架。开发者们可以集成任何用于创建用户界面的可以被编译成 HTML、JavaScript 和 CSS 的前端框架…

使用madExcept检测内存泄漏

代码异常堆栈跟踪&#xff1a;Mad Except 一、安装 官网 运行&#xff0c;选择madExcept5然后安装。 输入yes继续 二、使用 新建一个VCL项目 在project中多了一项设置 选择OK后会发现项目多了几个引用单元。 此时运行程序&#xff0c;再退出&#xff0c;会显示没有任何内存…