如何使用 Redis 实现分布式锁?(附教学视频)

news/2024/11/22 2:40:08/

redis 实现分布式锁

redis 实现分布式锁的方式有两种:

通过 redis 提供的 setnx 进行实现,往 redis 中使用 setnx 插入 key 时,如果 key 存在,则返回 0,可以通过插入 key 的返回值进行判断来实现分布式锁

通过使用 Redission(客户端)来实现分布式锁。可以调用 Redission 提供的 api,即 lock(),unlock()方法进行加锁和锁的释放。此外, Redission 还提供了 watchdog,即看门狗,来对加锁的 key 每隔 10 s对该 key 进行续时,(从而避免锁的过期)

Redission 的所有指令是通过 lua 脚本进行实现的,lua 脚本可以保证所有指令执行的原子性

关键词:setnx,Redission,lock,unlock,watchdog,lua,原子性

基于 Redis 的分布式锁实现思路

下面是使用 Redis 实现分布式锁的步骤:

生成分布式锁的 key,一般为业务相关的业务 key;

生成分布式锁的 value,一般为1或者线程 id;

生成分布式锁的过期时间,避免锁过程中宕机锁无法解开;

通过不同语言的 API 实现 SET key value [EX seconds] [NX]命令;

通过命令执行结果返回的标识判断是否加锁成功,一般 true 为上锁成功;

释放锁时,通过删除分布式锁的 key 实现。

Java 实现

这里提供不可重入的分布式锁的实现方式:

@Component
public class RedisLock {@Resourceprivate StringRedisTemplate redisTemplate;/*** 尝试获取分布式锁* @param key 锁的键值* @param value 锁的值* @param expireTime 锁的过期时间* @param timeUnit 锁的过期时间单位* @return 是否获取到锁*/public boolean tryLock(String key, String value, long expireTime, TimeUnit timeUnit) {// 利用 setIfAbsent 方法实现分布式锁Boolean success = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, timeUnit);return Boolean.isTrue(success);}/*** 释放分布式锁* @param key 锁的键值*/public void unlock(String key) {redisTemplate.delete(key);}
}

什么是可重入锁

分布式锁的可重入是指在分布式环境下,同一个线程可以多次获取同一个锁,而不会出现死锁或其他异常情况。这是因为可重入锁会记录当前线程已经获取锁的次数,每次释放锁时会将次数减一,只有当次数为零时才会真正释放锁。在分布式环境下,可重入锁会将当前线程的标识和获取锁的次数一起存储在分布式存储系统中,以便其他节点可以识别当前线程是否已经获取了锁,并且可以正确地释放锁。

可重入锁的好处是可以避免死锁和其他异常情况,同时也可以提高代码的可读性和可维护性。但是,在分布式环境下,可重入锁的实现需要考虑到网络延迟、节点故障等因素,因此需要谨慎设计和测试。

可重入锁的简单实现

下面是一个使用Java和Redis实现可重入分布式锁的示例代码:

import redis.clients.jedis.Jedis;public class RedisReentrantLock {private Jedis jedis;private String lockKey;private String lockValue;private int lockCount;public RedisReentrantLock(Jedis jedis, String lockKey) {this.jedis = jedis;this.lockKey = lockKey;this.lockValue = null;this.lockCount = 0;}public synchronized boolean lock() {if (lockCount > 0) {lockCount++;return true;}String value = String.valueOf(System.currentTimeMillis());if (jedis.setnx(lockKey, value) == 1) {lockValue = value;lockCount = 1;return true;} else {String currentValue = jedis.get(lockKey);if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) {String oldValue = jedis.getSet(lockKey, value);if (oldValue != null && oldValue.equals(currentValue)) {lockValue = value;lockCount = 1;return true;}}}return false;}public synchronized boolean unlock() {if (lockCount == 0) {return false;}lockCount--;if (lockCount == 0) {jedis.del(lockKey);lockValue = null;}return true;}
}

在这个示例中,我们使用了Jedis客户端来连接Redis服务器。构造函数接受一个Jedis实例和一个锁的键值作为参数。lock()方法用于获取锁,如果当前线程已经获取了锁,则增加锁的计数器,否则尝试使用setnx命令在Redis中创建一个新的键值对,如果创建成功,则表示当前线程获取了锁,否则尝试获取当前锁的值并比较是否过期,如果过期则使用getset命令更新锁的值,如果更新成功则表示当前线程获取了锁。unlock()方法用于释放锁,如果当前线程还持有锁,则减少锁的计数器,如果计数器为零,则删除锁的键值对。

需要注意的是,这个示例中的锁并不是完全可重入的,因为它只记录了锁的计数器,而没有记录获取锁的线程。如果多个线程使用相同的锁键值并且在同一时间尝试获取锁,则可能会出现死锁或其他异常情况。为了实现完全可重入的锁,需要在锁的值中记录获取锁的线程标识,并在释放锁时检查当前线程是否持有锁。

Redisson实现分布式锁

Redisson是一个基于Redis的Java驻留对象服务(Remote Service)和分布式锁框架。它提供了一种简单而强大的方式来实现分布式锁,以确保在分布式环境中的多个进程或线程之间的互斥访问。

下面是使用Redisson实现分布式锁的步骤:

  1. 引入Redisson依赖

在Maven项目中,可以通过以下方式引入Redisson依赖:

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.15.5</version>
</dependency>
  1. 创建Redisson客户端

在使用Redisson之前,需要先创建Redisson客户端。可以通过以下方式创建:

Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);

这里使用的是单节点模式,如果是集群模式,需要使用useClusterServers()方法。

  1. 获取分布式锁

获取分布式锁的方式非常简单,只需要调用getLock()方法即可:

RLock lock = redisson.getLock("myLock");
lock.lock();
try {// 执行业务逻辑
} finally {lock.unlock();
}

这里创建了一个名为myLock的锁,并通过lock()方法获取锁。在执行业务逻辑之前,需要先获取锁,否则会被阻塞。

  1. 释放分布式锁

在业务逻辑执行完毕后,需要释放分布式锁,否则其他进程或线程无法获取锁。可以通过unlock()方法释放锁:

lock.unlock();

总结:

使用Redisson实现分布式锁非常简单,只需要引入依赖、创建Redisson客户端、获取锁、释放锁即可。同时,Redisson还提供了很多其他的分布式服务,如分布式Map、分布式List等,可以方便地实现分布式应用。

教学视频

我之前也录制了一套redis分布式锁的视频(https://www.imooc.com/learn/1371),点击下方阅读全文即可观看啦。


http://www.ppmy.cn/news/83007.html

相关文章

红队免杀,一句话木马的套路

一句话木马的套路 the-backdoor-factory 安装 Kali下 方式一&#xff1a; git clone https://github.com/secretsquirrel/the-backdoor-factory 方式二&#xff1a; apt-get install backdoor-factory 使用说明 ./backdoor.py -h 检查待检测软件是否支持&#xff08;如…

02 Maven创建及使用

maven作用 主要用作基于java平台的项目 maven能提供一种项目配置 maven能自动从maven的中央仓库帮我们自动下载并管路项目依赖的jar包 提供了标准的目录结构 中央仓库两种类型:共有的中央仓库:私有中央仓库 使用mvn -v查看是否安装成功 修改本地仓库的的位置 在setting…

[C++][windows]vcpkg使用教程

安装vcpkg: git clone https://github.com/microsoft/vcpkg 或者手动下载随便放一个非中文或者有空格的路径下&#xff0c;比如我直接放D D:\vcpkg 将D:\vcpkg加入环境变量。 之后进入vcpkg目录&#xff0c;双击运行bootstrap-vcpkg.bat会自动下载vcpkg.exe等文件&#xf…

服务扫描与查点-渗透测试模拟环境(3)

本篇将介绍服务扫描与查点渗透模拟环境下整理的各类收集方法、各类工具技术使用的演示,阅读后可用在工作上。 很多网络服务是漏洞频发的高危对象,对网络上的特定服务进行扫描,往往能让我们少走弯路,增加渗透成功的几率。确定开放端口后,通常会对相应端口上所运行服务的信息…

我出版了一本关于TikTok电商运营的书

回首2020年初&#xff0c;第一次在手机上下载TikTok的那个下午&#xff0c;我并没有意识到&#xff0c;未来三年多这个词会充满我的工作与生活。 那其实是非常幸福的一段时间&#xff0c;对TikTok的期待没有那么功利&#xff0c;每天刷一刷TikTok中的视频&#xff0c;再随手拍…

go monkey

定义 golang用来做 monkey patching 的测试库。 monkey patch &#xff1a;在运行时&#xff0c;动态修改一些变量/函数/方法/模块 的行为的能力。 对于有些三方的库&#xff0c;我们没有权限去调整代码逻辑&#xff0c;而这又会对我们测试带来影响&#xff0c;所以&#xf…

软考高级架构师笔记-4中间件、嵌入式技术

目录 1. 前言 & 考情分析2. 中间件3. 微处理器4. 嵌入式软件5. 嵌入式系统6. 嵌入式软件设计6. 结语1. 前言 & 考情分析 前文回顾: 软考高级架构师笔记-1计算机硬件软考高级架构师笔记-2计算机软件(操作系统)软考高级架构师笔记-3数据库本章考情: 本章节偶尔会考到…

第三十六章 状态管理工具与总结

Redux 是一个渐进式的状态管理库&#xff0c;它不仅仅是一个库&#xff0c;同时也是一个框架。它提供了一组用于构建复杂应用程序的工具和库&#xff0c;其中包括一些浏览器插件。 在 Chrome 和 Firefox 浏览器上&#xff0c;已经存在一些 Redux 的浏览器插件&#xff0c;例如 …