Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)

简介: Redis系列学习文章分享---第六篇(Redis实战篇--Redis分布式锁+实现思路+误删问题+原子性+lua脚本+Redisson功能介绍+可重入锁+WatchDog机制+multiLock)

Redis的实战篇-分布式锁

1. 分布式锁-基本原理和不同实现方式对比

1.1 基本原理

分布式锁是用于在分布式环境中控制共享资源访问的一种机制。其基本原理是利用某种方式确保在同一时刻只有一个客户端能够获得锁,从而避免多个客户端同时修改共享资源导致的数据不一致问题。

1.2 不同实现方式对比

常见的分布式锁实现方式包括基于数据库、基于ZooKeeper、基于Redis等。各种实现方式在性能、可靠性、易用性等方面有所不同,需要根据具体场景选择适合的方式。

2. 分布式锁-Redis的分布式锁实现思路

2.1 实现思路

在Redis中,可以利用SETNX(SET if Not eXists)命令来实现分布式锁。该命令可以原子性地设置一个键的值,当键不存在时才会进行设置,因此可以用来实现锁的加锁操作。

2.2 代码示例

String lockKey = "lock_key";
String requestId = UUID.randomUUID().toString();
boolean lock = jedis.set(lockKey, requestId, "NX", "PX", expireTime) != null;

以上代码使用了Java的Jedis客户端来操作Redis。首先生成一个唯一的requestId作为锁的标识,然后使用set命令尝试给lockKey加锁,设置NX参数表示仅在键不存在时才会设置成功,设置PX参数表示设置过期时间。如果加锁成功,返回true;否则返回false。

3. 分布式锁-实现Redis分布式锁版本

3.1 实现方式

基于Redis的单机实例或集群实现分布式锁,通常会使用SET命令结合EXPIRE或PSETEX命令来设置锁的超时时间,以防止锁被永久占用。

3.2 示例代码

String lockKey = "lock_key";
String requestId = UUID.randomUUID().toString();
boolean lock = jedis.set(lockKey, requestId, "NX", "PX", expireTime) != null;

在上述代码中,expireTime是锁的过期时间,单位为毫秒。当加锁成功后,通过设置锁的过期时间,可以确保即使出现异常情况导致锁未能及时释放,也不会永久占用锁资源。

4. 分布式锁-Redis分布式锁误删问题

4.1 问题分析

在使用Redis实现分布式锁时,可能会遇到误删锁的问题。这种情况通常发生在锁的过期时间内,业务执行时间较长导致锁被自动释放,然后其他客户端又获取到了相同的锁。

4.2 解决方案

为了解决误删锁的问题,可以使用Lua脚本来确保释放锁的原子性,即在删除锁之前先检查锁的持有者是否为当前客户端。

4.3 代码示例

String lockKey = "lock_key";
String requestId = UUID.randomUUID().toString();
String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                  "   return redis.call('del', KEYS[1]) " +
                  "else " +
                  "   return 0 " +
                  "end";
Object result = jedis.eval(luaScript, Collections.singletonList(lockKey), Collections.singletonList(requestId));

上述代码使用Lua脚本来判断当前锁的持有者是否为当前请求的客户端,如果是则删除锁,否则不执行任何操作。这样可以确保释放锁的原子性,避免误删锁的问题。

5. 分布式锁-解决Redis分布式锁误删问题

5.1 解决方案

除了使用Lua脚本来确保释放锁的原子性外,还可以通过为锁设置唯一的标识符来避免误删锁的问题。每次获取锁时,都为锁设置一个唯一的标识符,释放锁时只有持有相同标识符的客户端才能释放锁。

5.2 代码示例

String lockKey = "lock_key";
String requestId = UUID.randomUUID().toString();
// 加锁
boolean lock = jedis.set(lockKey, requestId, "NX", "PX", expireTime) != null;
// 解锁
String currentRequestId = jedis.get(lockKey);
if (requestId.equals(currentRequestId)) {
    jedis.del(lockKey);
}

在上述代码中,加锁时为锁设置了唯一的requestId作为标识符,解锁时通过比较当前锁的标识符和请求的标识符来确保只有持有相同标识符的客户端才能释放锁。

6. 分布式锁-分布式锁的原子性问题

6.1 问题分析

在分布式环境下,多个客户端同时尝试获取同一个锁时,可能会出现竞争情况,导致锁的获取和释放不具备原子性,进而引发一系列问题。

6.2 解决方案

为了保证分布式锁的原子性,需要采用一种原子操作方式来实现锁的获取和释放。这里可以利用Redis的SETNX(SET if Not eXists)命令,该命令可以在键不存在时设置键的值,如果键已经存在,则不进行任何操作。

6.3 代码示例
String lockKey = "lock_key";
String requestId = UUID.randomUUID().toString();
// 尝试获取锁
boolean lockAcquired = jedis.setnx(lockKey, requestId) == 1;
// 设置锁的过期时间
if (lockAcquired) {
    jedis.expire(lockKey, expireTime);
}
// 处理业务逻辑
// 释放锁
if (lockAcquired) {
    jedis.del(lockKey);
}

在上述代码中,通过SETNX命令尝试获取锁,并使用expire命令设置锁的过期时间,确保即使在获取锁后发生异常或程序意外退出时,锁也能够自动释放,避免死锁情况的发生。

7. 分布式锁-Lua脚本解决多条命令原子性问题

7.1 解决方案

为了保证多条命令的原子性操作,可以使用Lua脚本来将多个命令封装成一个原子操作,确保在执行期间不会被其他客户端中断。

7.2 代码示例
String lockKey = "lock_key";
String requestId = UUID.randomUUID().toString();
String luaScript = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +
                   "   redis.call('expire', KEYS[1], ARGV[2]) " +
                   "   return true " +
                   "else " +
                   "   return false " +
                   "end";
Boolean lockAcquired = (Boolean) jedis.eval(luaScript, Collections.singletonList(lockKey),
                                            Arrays.asList(requestId, String.valueOf(expireTime)));

在上述代码中,通过Lua脚本封装了SETNX和EXPIRE命令,确保了获取锁和设置过期时间的原子性操作,从而避免了多个命令执行过程中的竞争情况。

8. 分布式锁-Java调用1ua脚本改造分布式锁

8.1 解决方案

在Java中,可以通过使用Jedis客户端执行Lua脚本来实现对Redis中分布式锁的获取和释放操作。这样可以保证多个Redis命令的原子性执行,进而确保分布式锁的正确性和可靠性。

8.2 代码示例
String lockKey = "lock_key";
String requestId = UUID.randomUUID().toString();
String luaScript = "if redis.call('setnx', KEYS[1], ARGV[1]) == 1 then " +
                   "   redis.call('expire', KEYS[1], ARGV[2]) " +
                   "   return true " +
                   "else " +
                   "   return false " +
                   "end";
// 调用Lua脚本执行分布式锁获取操作
Boolean lockAcquired = (Boolean) jedis.eval(luaScript, Collections.singletonList(lockKey),
                                            Arrays.asList(requestId, String.valueOf(expireTime)));
// 处理业务逻辑
// 释放锁
if (lockAcquired) {
    jedis.del(lockKey);
}

在上述代码中,通过调用jedis.eval方法执行Lua脚本,实现了对Redis中分布式锁的获取和释放操作,并确保了原子性执行。

9. 分布式锁-Redisson功能介绍

9.1 Redisson简介

Redisson是一个基于Redis的Java驻内存数据网格(In-Memory Data Grid)和分布式锁服务的框架,提供了丰富的分布式对象和服务支持。它封装了Redis的分布式对象和服务,简化了Java应用程序对Redis的操作。

9.2 Redisson功能特点
  • 提供了分布式对象(Distributed Objects)和服务(Distributed Services)的API,包括分布式锁、分布式集合、分布式映射等。
  • 内置了多种分布式锁实现,包括可重入锁、公平锁、联锁等,满足不同场景下的需求。
  • 支持异步和响应式编程模型,提供了丰富的异步操作API。
  • 提供了基于事件通知机制的分布式消息队列,支持发布/订阅模式和点对点模式。
9.3 Redisson快速入门
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.5</version>
</dependency>
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("myLock");
lock.lock();
try {
    // 执行业务逻辑
} finally {
    lock.unlock();
}

在上述代码中,首先通过Maven添加Redisson依赖,然后创建Redisson客户端实例,通过调用getLock方法获取分布式锁实例,最后通过lock和unlock方法实现对分布式锁的获取和释放操作。

10. 分布式锁-Redisson快速入门

10.1 Redisson简介

Redisson是一个基于Redis的Java驻内存数据网格(In-Memory Data Grid)和分布式锁服务的框架,提供了丰富的分布式对象和服务支持。它封装了Redis的分布式对象和服务,简化了Java应用程序对Redis的操作。

10.2 Redisson功能特点
  • 提供了分布式对象(Distributed Objects)和服务(Distributed Services)的API,包括分布式锁、分布式集合、分布式映射等。
  • 内置了多种分布式锁实现,包括可重入锁、公平锁、联锁等,满足不同场景下的需求。
  • 支持异步和响应式编程模型,提供了丰富的异步操作API。
  • 提供了基于事件通知机制的分布式消息队列,支持发布/订阅模式和点对点模式。
10.3 Redisson快速入门
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.5</version>
</dependency>
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient redisson = Redisson.create(config);
RLock lock = redisson.getLock("myLock");
lock.lock();
try {
    // 执行业务逻辑
} finally {
    lock.unlock();
}

在上述代码中,首先通过Maven添加Redisson依赖,然后创建Redisson客户端实例,通过调用getLock方法获取分布式锁实例,最后通过lock和unlock方法实现对分布式锁的获取和释放操作。

感谢您阅读本文,希望对您了解Redis在分布式锁实现方面有所帮助。如有任何问题或建议,请随时在评论区留言。

相关文章
|
6月前
|
存储 运维 监控
120_检查点管理:故障恢复 - 实现分布式保存机制
在大型语言模型(LLM)的训练过程中,检查点管理是确保训练稳定性和可靠性的关键环节。2025年,随着模型规模的不断扩大,从百亿参数到千亿参数,训练时间通常长达数周甚至数月,硬件故障、软件错误或网络中断等问题随时可能发生。有效的检查点管理机制不仅能够在故障发生时快速恢复训练,还能优化存储使用、提高训练效率,并支持实验管理和模型版本控制。
548 5
120_检查点管理:故障恢复 - 实现分布式保存机制
|
7月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
655 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
存储 缓存 算法
分布式锁服务深度解析:以Apache Flink的Checkpointing机制为例
【10月更文挑战第7天】在分布式系统中,多个进程或节点可能需要同时访问和操作共享资源。为了确保数据的一致性和系统的稳定性,我们需要一种机制来协调这些进程或节点的访问,避免并发冲突和竞态条件。分布式锁服务正是为此而生的一种解决方案。它通过在网络环境中实现锁机制,确保同一时间只有一个进程或节点能够访问和操作共享资源。
585 3
|
11月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
353 32
|
11月前
|
缓存 NoSQL 算法
Redis数据库的键值过期和删除机制
我们需要注意的是,虽然Redis提供了这么多高级的缓存机制,但在使用过程中,必须理解应用的特性,选择合适的缓存策略,才能最大化Redis的性能。因此,在设计和实施应用程序时,理解应用的数据访问模式,以及这些模式如何与Redis的缓存机制相互作用,尤为重要。
324 24
|
8月前
|
存储 NoSQL Redis
采用Redis的Bitmaps实现类似Github连续提交状态的功能。
在现实世界的应用开发中,实现类似于Github提交跟踪系统时,还可能需要考虑用户时区、闰年等日期相关的边界条件,以及辅助数据的存储和查询优化,例如对活跃用户的即时查询和统计等。不过这些都可以在Bitmaps的基础功能之上通过额外的代码逻辑来实现。
183 0
|
11月前
|
存储 监控 NoSQL
使用Redis实现延迟消息发送功能
使用 Redis 的密码认证功能,为实例设置密码以防止未授权访问。为消息提供适当加密,确保消息内容在网络传输过程中不被窃取或篡改。
414 16
|
NoSQL Redis 数据库
Redis 功能扩展 Lua 脚本 对Redis扩展 eval redis.call redis.pcall
通过本文的介绍,我们详细讲解了 Lua 脚本在 Redis 中的作用、`eval` 命令的使用方法以及 `redis.call` 和 `redis.pcall` 的区别和用法。通过合理使用 Lua 脚本,可以实现复杂的业务逻辑,确保操作的原子性,并减少网络开销,从而提高系统的性能和可靠性。
976 13
|
消息中间件 算法 调度
分布式系统学习10:分布式事务
本文是小卷关于分布式系统架构学习系列的第13篇,重点探讨了分布式事务的相关知识。随着业务增长,单体架构拆分为微服务后,传统的本地事务无法满足需求,因此需要引入分布式事务来保证数据一致性。文中详细介绍了分布式事务的必要性、实现方案及其优缺点,包括刚性事务(如2PC、3PC)和柔性事务(如TCC、Saga、本地消息表、MQ事务、最大努力通知)。同时,还介绍了Seata框架作为开源的分布式事务解决方案,提供了多种事务模式,简化了分布式事务的实现。
666 5
|
NoSQL 关系型数据库 MySQL
分布式系统学习9:分布式锁
本文介绍了分布式系统中分布式锁的概念、实现方式及其应用场景。分布式锁用于在多个独立的JVM进程间确保资源的互斥访问,具备互斥、高可用、可重入和超时机制等特点。文章详细讲解了三种常见的分布式锁实现方式:基于Redis、Zookeeper和关系型数据库(如MySQL)。其中,Redis适合高性能场景,推荐使用Redisson库;Zookeeper适用于对一致性要求较高的场景,建议基于Curator框架实现;而基于数据库的方式性能较低,实际开发中较少使用。此外,还探讨了乐观锁和悲观锁的区别及适用场景,并介绍了如何通过Lua脚本和Redis的`SET`命令实现原子操作,以及Redisson的自动续期机
1282 7