Redisson是如何实现分布式锁的?

Redisson 如何实现分布式锁?(核心原理与思考)

Redisson 是一个功能强大的 Redis 客户端,它提供了许多分布式对象和服务,其中就包括分布式锁。Redisson 的分布式锁是基于 Redis 的 Lua 脚本实现的,这保证了操作的原子性。

我们来一步步拆解 Redisson 锁的实现原理

4.1 最基础的 Redis 锁(存在的问题)

如果只用 SETNXDEL

  1. 加锁: SETNX mylock_key my_client_id
    • my_client_id 是一个唯一标识,比如 UUID,用来标识是哪个客户端加的锁。
  2. 解锁: DEL mylock_key

问题:

  • 死锁: 如果客户端加锁后,还没来得及 DEL 就宕机了,那么 mylock_key 永远不会被删除,其他客户端就永远拿不到锁了。
  • 误删: 如果客户端A加锁,但因为网络延迟等原因,锁过期了(被Redis自动删除了),然后客户端B加锁成功。此时客户端A恢复,执行 DEL mylock_key,它删除了客户端B的锁!
4.2 Redisson 的改进:过期时间 + 唯一ID + Lua 脚本

Redisson 解决了上述问题,它的核心思想是:

  1. 加锁时设置过期时间: 避免死锁。
  2. 加锁时设置唯一ID: 避免误删。
  3. 使用 Lua 脚本: 保证加锁和解锁操作的原子性。

Redisson 加锁的 Lua 脚本(简化版):

lua 复制代码
-- KEYS[1]: 锁的名称 (e.g., "myLock")
-- ARGV[1]: 锁的过期时间 (e.g., 30000 毫秒)
-- ARGV[2]: 客户端的唯一ID (e.g., UUID + 线程ID)

-- 如果锁不存在,或者锁是当前客户端加的
if redis.call('exists', KEYS[1]) == 0 or redis.call('hexists', KEYS[1], ARGV[2]) == 1 then
    -- 设置锁,并设置过期时间
    -- HINCRBY: 将哈希表中字段的值增加指定增量。这里是记录重入次数
    redis.call('hincrby', KEYS[1], ARGV[2], 1);
    redis.call('pexpire', KEYS[1], ARGV[1]); -- 设置过期时间 (毫秒)
    return nil; -- 表示加锁成功
end;
return redis.call('pttl', KEYS[1]); -- 返回锁的剩余过期时间,表示加锁失败

Redisson 解锁的 Lua 脚本(简化版):

lua 复制代码
-- KEYS[1]: 锁的名称
-- ARGV[1]: 客户端的唯一ID

-- 如果锁不存在,或者不是当前客户端加的锁
if redis.call('exists', KEYS[1]) == 0 or redis.call('hexists', KEYS[1], ARGV[1]) == 0 then
    return nil; -- 表示解锁失败或锁不存在
end;

-- 减少重入次数
local counter = redis.call('hincrby', KEYS[1], ARGV[1], -1);
-- 如果重入次数归零,说明完全释放了锁
if counter > 0 then
    -- 重新设置过期时间
    redis.call('pexpire', KEYS[1], ARGV[2]); -- ARGV[2] 是新的过期时间
    return 0; -- 表示锁还在,只是重入次数减少
else
    -- 重入次数归零,删除锁
    redis.call('del', KEYS[1]);
    return 1; -- 表示锁已完全释放
end;
return nil;

思考:为什么用 Hash 类型?

  • HINCRBY:这实现了可重入锁。同一个客户端(同一个线程)可以多次获取同一个锁,每次获取都会增加哈希表中对应字段的值(重入次数)。释放锁时,每次减少重入次数,直到为0才真正释放锁。
  • 哈希表的字段是客户端的唯一ID,值是重入次数。
  1. 锁的类型:
    • 可重入锁 (Reentrant Lock): 最常用,上面已解释。
    • 公平锁 (Fair Lock): 保证获取锁的顺序。
    • 联锁 (MultiLock): 同时获取多个 Redis 实例上的锁,只要有一个实例加锁失败,所有已加的锁都会被释放。用于解决 Redis 单点故障问题。
    • 红锁 (RedLock): Redisson 实现了 Redis 官方推荐的 RedLock 算法。它需要多个独立的 Redis Master 节点(至少3个,通常5个)。客户端尝试在大多数(N/2 + 1)Redis 实例上获取锁,才能算成功。这提供了更高的可用性和容错性,但实现更复杂,性能开销也更大。
    • 读写锁 (ReadWriteLock): 允许多个读操作同时进行,但写操作是独占的。
    • 信号量 (Semaphore): 控制并发访问资源的数量。
    • 闭锁 (CountDownLatch): 类似 Java 的 CountDownLatch。
相关推荐
Bug退退退1234 小时前
RabbitMQ 高级特性之事务
java·分布式·spring·rabbitmq
CodeWithMe5 小时前
【Note】《Kafka: The Definitive Guide》第四章:Kafka 消费者全面解析:如何从 Kafka 高效读取消息
分布式·kafka
Gauss松鼠会8 小时前
GaussDB应用场景全景解析:从金融核心到物联网的分布式数据库实践
数据库·分布式·物联网·金融·database·gaussdb
❀always❀17 小时前
深入浅出分布式限流(更新中)
分布式·wpf
Bug退退退12320 小时前
RabbitMQ 幂等性
分布式·rabbitmq
{⌐■_■}1 天前
【Kafka】登录日志处理的三次阶梯式优化实践:从同步写入到Kafka多分区批处理
数据库·分布式·mysql·kafka·go
qq_529835351 天前
RabbitMQ的消息可靠传输
分布式·rabbitmq
CodeWithMe1 天前
【Note】《Kafka: The Definitive Guide》 第九章:Kafka 管理与运维实战
运维·分布式·kafka
sql2008help1 天前
1-Kafka介绍及常见应用场景
分布式·kafka