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在分布式锁实现方面有所帮助。如有任何问题或建议,请随时在评论区留言。

相关文章
|
7月前
|
存储 负载均衡 NoSQL
【赵渝强老师】Redis Cluster分布式集群
Redis Cluster是Redis的分布式存储解决方案,通过哈希槽(slot)实现数据分片,支持水平扩展,具备高可用性和负载均衡能力,适用于大规模数据场景。
496 2
|
7月前
|
存储 缓存 NoSQL
【📕分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
本文解析 Redisson 如何通过 Redis 实现分布式信号量(RSemaphore)与倒数闩(RCountDownLatch),利用 Lua 脚本与原子操作保障分布式环境下的同步控制,帮助开发者更好地理解其原理与应用。
447 6
|
8月前
|
存储 缓存 NoSQL
Redis核心数据结构与分布式锁实现详解
Redis 是高性能键值数据库,支持多种数据结构,如字符串、列表、集合、哈希、有序集合等,广泛用于缓存、消息队列和实时数据处理。本文详解其核心数据结构及分布式锁实现,帮助开发者提升系统性能与并发控制能力。
|
12月前
|
数据采集 存储 数据可视化
分布式爬虫框架Scrapy-Redis实战指南
本文介绍如何使用Scrapy-Redis构建分布式爬虫系统,采集携程平台上热门城市的酒店价格与评价信息。通过代理IP、Cookie和User-Agent设置规避反爬策略,实现高效数据抓取。结合价格动态趋势分析,助力酒店业优化市场策略、提升服务质量。技术架构涵盖Scrapy-Redis核心调度、代理中间件及数据解析存储,提供完整的技术路线图与代码示例。
1389 0
分布式爬虫框架Scrapy-Redis实战指南
|
6月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
540 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
6月前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。
|
10月前
|
数据采集 存储 NoSQL
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
673 67
|
8月前
|
NoSQL Redis
Lua脚本协助Redis分布式锁实现命令的原子性
利用Lua脚本确保Redis操作的原子性是分布式锁安全性的关键所在,可以大幅减少由于网络分区、客户端故障等导致的锁无法正确释放的情况,从而在分布式系统中保证数据操作的安全性和一致性。在将这些概念应用于生产环境前,建议深入理解Redis事务与Lua脚本的工作原理以及分布式锁的可能问题和解决方案。
297 8
|
9月前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
2500 7
|
10月前
|
缓存 监控 NoSQL
Redis设计与实现——分布式Redis
Redis Sentinel 和 Cluster 是 Redis 高可用与分布式架构的核心组件。Sentinel 提供主从故障检测与自动切换,通过主观/客观下线判断及 Raft 算法选举领导者完成故障转移,但存在数据一致性和复杂度问题。Cluster 支持数据分片和水平扩展,基于哈希槽分配数据,具备自动故障转移和节点发现机制,适合大规模高并发场景。复制机制包括全量同步和部分同步,通过复制积压缓冲区优化同步效率,但仍面临延迟和资源消耗挑战。两者各有优劣,需根据业务需求选择合适方案。
734 14