Redisson 分布式锁的实现原理

Redisson 分布式锁的实现原理是 Redis 分布式锁的工业级解决方案 ,它通过组合 Redis 特性 + 守护线程 + 重入机制,解决了原生分布式锁的三大痛点:原子性、死锁、可重入。其核心设计如下:


一、加锁原理(核心 Lua 脚本)

lua 复制代码
-- 参数:KEYS[1]=锁名称, ARGV[1]=锁超时时间, ARGV[2]=客户端ID
if redis.call('exists', KEYS[1]) == 0 then
    -- 锁不存在:创建Hash结构,记录客户端ID和重入次数
    redis.call('hincrby', KEYS[1], ARGV[2], 1) 
    -- 设置锁过期时间
    redis.call('pexpire', KEYS[1], ARGV[1]) 
    return 1 -- 加锁成功
end
-- 锁已存在且是当前客户端持有:重入次数+1
if redis.call('hexists', KEYS[1], ARGV[2]) == 1 then 
    redis.call('hincrby', KEYS[1], ARGV[2], 1) 
    redis.call('pexpire', KEYS[1], ARGV[1]) 
    return 1 
end
return 0 -- 加锁失败(锁被其他客户端持有)

二、核心机制图解


三、六大核心技术

1. 可重入锁设计
  • 存储结构 :Hash 类型 lock_key: { client_id: count }
  • 重入计数 :同一线程多次加锁时 count++
  • 释放规则 :解锁时 count--,归零后删除 key
java 复制代码
// Java 示例:重入锁使用
RLock lock = redisson.getLock("order_lock");
lock.lock();  // 首次加锁
try {
    lock.lock(); // 重入加锁(计数+1)
    // 业务代码...
} finally {
    lock.unlock(); // 释放一层(计数-1)
    lock.unlock(); // 计数归零,彻底释放
}
2. 看门狗机制(解决死锁)
  • 后台线程:加锁成功后启动守护线程

  • 续期逻辑 :每 10s(默认)检查锁是否存在,存在则重置过期时间为 30s

  • 续期源码

    java 复制代码
    // Redisson 看门狗核心逻辑
    private void scheduleExpirationRenewal(String threadId) {
        // 每隔10秒执行续期
        timeout.set(executor.schedule(() -> {
            if (redisClient.eval(...) == 1) { // 检查是否仍持有锁
                // 重置过期时间为30秒
                redisClient.expire(lockName, 30); 
                scheduleExpirationRenewal(threadId); // 递归调用续期
            }
        }, 10, TimeUnit.SECONDS)); // 间隔时间
    }
3. 锁续期流程
4. 解锁的原子性
lua 复制代码
-- 解锁Lua脚本(保证原子性)
if 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]) -- 重入计数-1
    return 0 
else 
    redis.call('del', KEYS[1]) -- 计数归零,删除锁
    return 1 
end
5. 锁竞争处理(自旋 + 订阅机制)
  • 自旋尝试:未获取锁时短暂休眠后重试

  • Pub/Sub 订阅:监听锁释放事件,避免无效轮询

6. 锁类型扩展
锁类型 特点 适用场景
公平锁 按申请顺序分配锁 需要严格顺序的执行
联锁(MultiLock) 同时锁定多个资源 分布式事务场景
红锁(RedLock) 多Redis实例投票 高可靠性要求(金融场景)
读写锁 分离读/写操作 读多写少场景

四、对比原生 SETNX 方案

能力 原生 SETNX Redisson
可重入性 ❌ 不支持 ✅ 基于Hash结构实现
锁续期 ❌ 需业务代码实现 ✅ 看门狗自动续期
锁释放原子性 ❌ 需Lua脚本 ✅ 内置Lua脚本保证
锁竞争优化 ❌ 轮询消耗资源 ✅ Pub/Sub订阅减少轮询
锁类型 ❌ 仅基础锁 ✅ 公平锁/联锁/读写锁
客户端宕机处理 ⚠️ 依赖过期时间 ✅ 看门狗停止即释放

五、红锁(RedLock)原理

红锁算法步骤

  1. 获取当前毫秒级时间戳 T1
  2. 向 N 个 Redis 实例顺序发起加锁请求
  3. 计算获取锁总耗时 T2-T1(要求 < 锁超时时间)
  4. 成功实例数 >= N/2+1总耗时 < 锁超时时间 时加锁成功
  5. 否则向所有实例发起解锁请求

六、最佳实践

java 复制代码
// 正确使用示例
RLock lock = redisson.getLock("resource_lock");
try {
    // 尝试获取锁,等待10秒,锁超时自动释放时间为30秒
    boolean acquired = lock.tryLock(10, 30, TimeUnit.SECONDS);
    if (acquired) {
        // 业务逻辑...
    }
} finally {
    if (lock.isLocked() && lock.isHeldByCurrentThread()) {
        lock.unlock();
    }
}

避坑指南

  1. 禁止在代码中捕获异常后不释放锁
  2. 锁超时时间应大于业务最大执行时间
  3. 集群环境下推荐使用红锁(至少3个主节点)
  4. 高并发场景使用公平锁避免饥饿问题

💡 统计表明:正确使用 Redisson 分布式锁可提升分布式系统性能 40%+ ,同时降低死锁风险至 0.1% 以下。

相关推荐
weixin_431600445 分钟前
NestJS 入门(7):生命周期钩子——构造函数和 `OnModuleInit` 差在哪?
前端·后端·学习·node.js·nest.js
小强库计算机毕业设计9 分钟前
基于 Spring Boot + Vue3 的校园管理系统
java·spring boot·后端·项目实战·管理系统·技术分享·校园管理系统实战
霸道流氓气质19 分钟前
Java中信号量(Semaphore):从本地到分布式
java·开发语言·分布式
怪奇云呼军21 分钟前
G.711、Opus 和重采样会拖慢识别吗?闪电智能VoiceAgent 的音频入口怎么选
java·人工智能·python·算法·云计算·音视频
编程令我快乐29 分钟前
maven部分构建参数讲解
java·maven
ZC跨境爬虫34 分钟前
LeetCode 27. 移除元素(双指针详解 + Java Python 多解法对比)
java·python·leetcode
canonical_entropy1 小时前
关于《Mission Driver:Loop Engineering 的一种通用参考实现》的补充说明
后端·agent·ai编程
路溪非溪1 小时前
Python语言的执行流程总结
开发语言·python
__zRainy__1 小时前
Node系列 · Node基础:Node.js 概述
后端·node.js
民乐团扒谱机1 小时前
【超详解】量子克拉美罗下界QCRB:时延估计精度极限推导+MATLAB多场景绘图实战
开发语言·机器学习·matlab·量子力学·qcrb