深入浅出Redission

文章目录

  • 一、Redission到底是什么
  • 二、网络通信模型
  • 三、分布式锁的实现
    • [3.1 Redission 分布式锁的核心特性](#3.1 Redission 分布式锁的核心特性)
    • [3.2 Redission 主要分布式锁类型及实现原理](#3.2 Redission 主要分布式锁类型及实现原理)
      • [1. 可重入锁(ReentrantLock)- 最常用](#1. 可重入锁(ReentrantLock)- 最常用)
      • [2. 公平锁(FairLock)](#2. 公平锁(FairLock))
      • [3. 联锁(MultiLock)](#3. 联锁(MultiLock))
      • [4. 红锁(RedLock)- 高可用锁](#4. 红锁(RedLock)- 高可用锁)
      • [5. 读写锁(ReadWriteLock)](#5. 读写锁(ReadWriteLock))
      • [6. 信号量(Semaphore)](#6. 信号量(Semaphore))
      • [7. 可过期信号量(PermitExpirableSemaphore)](#7. 可过期信号量(PermitExpirableSemaphore))
      • [8. 闭锁(CountDownLatch)](#8. 闭锁(CountDownLatch))
    • [3.3 分布式锁的分布式机制实现](#3.3 分布式锁的分布式机制实现)
    • [3.4 核心共性总结](#3.4 核心共性总结)
    • [3.5 分布式锁选型建议](#3.5 分布式锁选型建议)
  • 四、高可用部署适配
    • [4.1 拓扑动态感知:集群状态的实时雷达](#4.1 拓扑动态感知:集群状态的实时雷达)
    • [4.2 故障转移适配:主节点宕机后的自动切换](#4.2 故障转移适配:主节点宕机后的自动切换)
    • [4.3 Cluster 集群适配:槽位路由与迁移处理](#4.3 Cluster 集群适配:槽位路由与迁移处理)
    • [4.4 RedLock 极致高可用:多节点投票机制](#4.4 RedLock 极致高可用:多节点投票机制)

在Java分布式系统开发中,Redis的应用无处不在,但你是否也曾遇到这些痛点:用原生Redis命令实现分布式锁时反复踩坑死锁与锁超时问题?手写分布式集合操作时难以保证原子性?面对高并发场景,Redis客户端通信效率瓶颈明显?

今天要介绍的Redission,正是为解决这些问题而生的"瑞士军刀"。它不是简单的Redis客户端封装,而是一套完整的分布式应用开发框架。

一、Redission到底是什么

很多人会把Redission和Jedis、Lettuce归为一类,这其实是个误区。准确来说:

Redission是基于Redis实现的Java分布式应用开发框架,它以Redis为底层存储,封装了分布式锁、分布式集合、分布式信号量等数十种分布式场景核心功能,同时通过底层通信优化实现高性能交互。

简单对比三者的定位:

组件 核心定位 适用场景
Jedis Redis原生命令的Java实现(BIO模型) 简单Redis操作、低并发场景
Lettuce 基于Netty的异步Redis客户端 高并发场景下的基础Redis操作
Redission 分布式功能框架(内置Lettuce核心) 分布式锁、分布式集合等复杂场景
一句话总结:用Jedis/Lettuce是"操作Redis",用Redission是"基于Redis开发分布式系统"。

二、网络通信模型

Redission底层网络模型基于Netty(Java NIO),关于Java NIO可以看我的另一篇:https://blog.csdn.net/Tracycoder/article/details/155450115

网络通信模型的这种设计和实现为Redission的高效性能打下了坚实的基础。

三、分布式锁的实现

Redission 是基于 Redis 实现的分布式服务框架,其核心优势之一是提供了丰富的分布式锁实现,覆盖了绝大多数业务场景(可重入、公平、联锁、红锁等)。这些锁的底层依赖 Redis 的数据结构(Hash、ZSet、String)和 Lua 脚本(保证原子性),并解决了原生 Redis 分布式锁的诸多痛点(如重入性、自动续期、死锁避免等)。

3.1 Redission 分布式锁的核心特性

所有 Redission 分布式锁都具备以下基础能力,无需手动实现:

  1. 原子性:通过 Lua 脚本将「判断-加锁-解锁」等操作打包执行,避免并发竞态条件;
  2. 可重入性:支持同一线程多次加锁,通过计数器记录重入次数;
  3. 自动续期(看门狗机制):默认锁过期时间 30 秒,若业务未执行完,会每隔 10 秒自动续期,避免锁提前释放;
  4. 阻塞/非阻塞支持 :支持 lock()(阻塞等待)和 tryLock()(非阻塞,超时返回);
  5. 死锁避免:锁自带过期时间,即使服务宕机,Redis 也会自动释放锁;
  6. 集群适配:支持 Redis 单机、主从、哨兵、Cluster 集群模式,保证高可用。

3.2 Redission 主要分布式锁类型及实现原理

1. 可重入锁(ReentrantLock)- 最常用

  • 特点

    • 分布式环境下的「可重入互斥锁」,类比 Java 的 ReentrantLock;
    • 同一线程可多次加锁,解锁需对应次数(重入计数器减为 0 才释放锁);
    • 适用于:大多数分布式互斥场景(如订单创建、库存扣减)。
  • 实现原理

    基于 Redis 的 Hash 数据结构 存储锁信息,Lua 脚本保证加锁/解锁原子性。

  • 加锁流程

  1. 锁的 Redis Key 格式:redisson_lock:{锁名称}(如 redisson_lock:order_create);
  2. Hash 结构的 Field 为「当前线程标识(UUID+线程ID)」,Value 为「重入次数」;
  3. 执行 Lua 脚本:
    • 若锁不存在:创建 Hash 并设置 Field=线程标识,Value=1,同时设置 Key 过期时间(默认 30 秒);
    • 若锁已存在且 Field 是当前线程:重入次数 +1,重置过期时间;
    • 若锁已存在且 Field 是其他线程:返回失败,进入阻塞等待(或直接返回,取决于 lock()/tryLock())。
  • 解锁流程

    执行 Lua 脚本:

    • 若锁不存在:直接返回成功;
    • 若锁存在但 Field 不是当前线程:返回失败(非法解锁);
    • 若锁存在且 Field 是当前线程:重入次数 -1,若减为 0 则删除锁 Key(释放锁),否则仅重置过期时间。
  • 核心 Lua 脚本(加锁)

lua 复制代码
-- 若锁不存在,创建锁(重入次数=1)
if (redis.call('exists', KEYS[1]) == 0) then
    redis.call('hset', 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;
  • 代码示例
java 复制代码
RLock lock = redissonClient.getLock("order_create_lock");
try {
    // 阻塞加锁(默认30秒过期,自动续期)
    lock.lock();
    // 业务逻辑:创建订单、扣减库存...
} finally {
    // 必须解锁(若重入多次,需对应解锁多次)
    lock.unlock();
}

2. 公平锁(FairLock)

  • 特点

    • 分布式环境下的「公平互斥锁」,保证线程获取锁的顺序与请求顺序一致(FIFO);
    • 避免「饥饿问题」(某个线程长期无法获取锁);
    • 适用于:对获取锁顺序有要求的场景(如任务队列、优先级任务执行)。
  • 实现原理

    基于 Redis 的 ZSet(有序集合)+ Hash 实现,ZSet 用于维护等待队列(按请求时间排序),Hash 存储锁信息。

  • 核心设计

  1. 等待队列:ZSet 的 Key 为 redisson_lock_queue:{锁名称},Score 为「请求时间戳」,Value 为「线程标识」,保证 FIFO 顺序;
  2. 加锁流程:
    • 先尝试加锁(逻辑同可重入锁);
    • 若加锁失败,将当前线程加入 ZSet 等待队列,并通过 Redis 的 BLPOP 阻塞等待(监听前一个线程的释放信号);
  3. 解锁流程:
    • 释放锁后,从 ZSet 中取出下一个等待线程,通过 PUBLISH 发送信号唤醒,使其尝试加锁。
  • 注意
    公平锁的性能略低于可重入锁(需维护等待队列),非必要场景不建议使用。

3. 联锁(MultiLock)

  • 特点

    • 「多锁联动」,需同时获取所有子锁才算加锁成功,只要有一个子锁获取失败,所有已获取的子锁会自动释放;
    • 适用于:需要同时锁定多个资源的场景(如转账:需同时锁定转出账户和转入账户)。
  • 实现原理

    基于「组合模式」,内部维护一个子锁列表(RLock[]),通过原子操作保证多锁的一致性。

  • 加锁流程

  1. 按顺序尝试获取每个子锁(默认超时时间为总超时时间的 1/子锁数量);
  2. 若所有子锁都获取成功,返回加锁成功;
  3. 若某个子锁获取失败,立即释放已获取的所有子锁(避免死锁);
  4. 支持自动续期(所有子锁同时续期)。
  • 代码示例
java 复制代码
// 定义两个子锁(锁定转出账户和转入账户)
RLock lock1 = redissonClient.getLock("account_lock:userA");
RLock lock2 = redissonClient.getLock("account_lock:userB");

// 创建联锁
RLock multiLock = redissonClient.getMultiLock(lock1, lock2);

try {
    // 必须同时获取两个锁才会继续执行(超时 5 秒,leaseTime -1 表示自动续期)
    multiLock.tryLock(5, -1, TimeUnit.SECONDS);
    // 业务逻辑:转账操作...
} finally {
    multiLock.unlock();
}

4. 红锁(RedLock)- 高可用锁

  • 特点

    • 基于 Redis 集群的「高可用互斥锁」,解决单节点/主从切换导致的锁失效问题;
    • 需向 多个独立的 Redis 节点(推荐 3-5 个)同时加锁,超过半数节点加锁成功才算整体加锁成功;
    • 适用于:对锁可用性要求极高的场景(如金融交易、核心数据修改)。
  • 实现原理

    遵循 Redis 作者提出的「RedLock 算法」,核心是「少数服从多数」,避免单节点故障导致锁丢失。

  • 加锁流程

  1. 客户端向所有独立 Redis 节点(无主从关系)发送加锁请求(每个节点的锁 Key 相同,线程标识相同);
  2. 每个节点的加锁逻辑同「可重入锁」,但不依赖主从复制(节点独立);
  3. 客户端统计加锁成功的节点数,若成功数 > 节点总数/2(如 3 个节点需 2 个成功),且总耗时未超过超时时间,则加锁成功;
  4. 若加锁失败,立即向所有节点发送解锁请求(释放已加的锁)。
  • 解锁流程

    • 向所有节点发送解锁请求(无论该节点是否加锁成功),确保所有节点的锁都被释放。
  • 代码示例

java 复制代码
// 配置 3 个独立的 Redis 节点(无主从关系)
Config config1 = new Config();
config1.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient client1 = Redisson.create(config1);

Config config2 = new Config();
config2.useSingleServer().setAddress("redis://127.0.0.1:6380");
RedissonClient client2 = Redisson.create(config2);

Config config3 = new Config();
config3.useSingleServer().setAddress("redis://127.0.0.1:6381");
RedissonClient client3 = Redisson.create(config3);

// 创建红锁(传入 3 个节点的锁)
RLock lock1 = client1.getLock("redlock:finance");
RLock lock2 = client2.getLock("redlock:finance");
RLock lock3 = client3.getLock("redlock:finance");

RLock redLock = Redisson.createRedLock(lock1, lock2, lock3);

try {
    // 加锁:超时 5 秒,锁过期 30 秒(自动续期)
    boolean success = redLock.tryLock(5, 30, TimeUnit.SECONDS);
    if (success) {
        // 业务逻辑:金融交易...
    }
} finally {
    redLock.unlock();
}
  • 注意
    • 红锁的性能略低(需访问多个节点),且需要独立的 Redis 节点(不能是同一集群的从节点),部署成本较高。

5. 读写锁(ReadWriteLock)

  • 特点

    • 分布式环境下的「共享-排他锁」,类比 Java 的 ReentrantReadWriteLock;
    • 读锁(共享锁):多个线程可同时获取,互不阻塞;
    • 写锁(排他锁):仅一个线程可获取,阻塞所有读锁和写锁;
    • 适用于:「读多写少」场景(如商品详情查询、数据统计,写操作仅在更新时执行)。
  • 实现原理

    基于 Redis 的 两个 Hash 结构 分别存储读锁和写锁信息,通过 Lua 脚本保证读写互斥逻辑。

  • 核心规则

    • 写锁加锁:需确保当前无读锁和其他写锁;
    • 读锁加锁:需确保当前无写锁;
    • 写锁优先级高于读锁(默认,可配置):当有写锁等待时,后续读锁会阻塞,避免写锁饥饿。
  • 代码示例

java 复制代码
RReadWriteLock rwLock = redissonClient.getReadWriteLock("product_rw_lock");
RLock readLock = rwLock.readLock(); // 读锁
RLock writeLock = rwLock.writeLock(); // 写锁

// 读操作(共享锁)
try {
    readLock.lock();
    // 业务逻辑:查询商品详情...
} finally {
    readLock.unlock();
}

// 写操作(排他锁)
try {
    writeLock.lock();
    // 业务逻辑:更新商品价格...
} finally {
    writeLock.unlock();
}

6. 信号量(Semaphore)

  • 特点

    • 分布式环境下的「信号量」,控制并发访问的资源数量(类比 Java 的 Semaphore);
    • 支持「获取许可」和「释放许可」,许可数量可动态调整;
    • 适用于:限流场景(如限制同时访问某个接口的线程数)、资源池控制(如限制数据库连接数)。
  • 实现原理

    基于 Redis 的 String 数据结构 存储许可数量,Lua 脚本保证许可操作的原子性。

  • 核心流程

  1. 初始化:设置信号量的许可数量(如 semaphore:limit = 10,表示最多 10 个并发);
  2. 获取许可:acquire() 或 tryAcquire(),将许可数量 -1,若为负数则阻塞(或返回失败);
  3. 释放许可:release(),将许可数量 +1,唤醒阻塞的线程。
  • 代码示例
java 复制代码
RSemaphore semaphore = redissonClient.getSemaphore("api_limit_semaphore");
// 初始化许可数量(可选,若未初始化则默认为 0)
semaphore.trySetPermits(10);

try {
    // 获取 1 个许可(阻塞等待,直到有可用许可)
    semaphore.acquire();
    // 业务逻辑:接口处理...
} finally {
    // 释放 1 个许可
    semaphore.release();
}

// 非阻塞获取(超时 3 秒,获取 2 个许可)
boolean success = semaphore.tryAcquire(2, 3, TimeUnit.SECONDS);
if (success) {
    try {
        // 业务逻辑...
    } finally {
        semaphore.release(2);
    }
}

7. 可过期信号量(PermitExpirableSemaphore)

  • 特点

    • 增强版信号量,每个许可带有「过期时间」;
    • 适用于:临时资源占用场景(如临时占用服务器端口、临时锁定某个任务,超时自动释放)。
  • 实现原理

    基于 Redis 的 ZSet 数据结构 存储许可(Score 为过期时间戳,Value 为许可 ID),通过 Lua 脚本清理过期许可并分配新许可。

8. 闭锁(CountDownLatch)

  • 特点

    • 分布式环境下的「闭锁」,类比 Java 的 CountDownLatch;
    • 一个线程等待其他多个线程完成操作后,再继续执行;
    • 适用于:分布式任务协调(如分布式计算的汇总任务,等待所有分片任务完成后再统计结果)。
  • 实现原理

    基于 Redis 的 Hash 数据结构 存储计数器,结合 Redis 的「发布-订阅」机制唤醒等待线程。

  • 核心流程

  1. 初始化:设置计数器值(如 countdown:task = 3,表示需要 3 个任务完成);
  2. 任务线程:执行完后调用 countDown(),计数器 -1;
  3. 等待线程:调用 await() 阻塞,直到计数器减为 0,通过 Redis 订阅消息唤醒。
  • 代码示例
java 复制代码
RCountDownLatch latch = redissonClient.getCountDownLatch("task_coordinate_latch");
// 初始化计数器为 3(3 个任务需要完成)
latch.trySetCount(3);

// 等待线程(如汇总线程)
new Thread(() -> {
    try {
        // 阻塞等待,直到计数器为 0
        latch.await();
        // 所有任务完成,执行汇总逻辑...
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}).start();

// 任务线程 1
new Thread(() -> {
    // 业务逻辑:任务 1 执行...
    latch.countDown(); // 计数器减 1
}).start();

// 任务线程 2、3 同理...

3.3 分布式锁的分布式机制实现

锁Key的「哈希槽路由集中化」:避免锁分散

Redis Cluster 采用「哈希槽分片」(16384 个槽),每个主节点负责部分槽位,Key 按 CRC16(Key) % 16384 计算槽位,路由到对应主节点。

Redission 为了避免「同一把锁的 Key 分散到多个节点」,做了关键设计:
同一把分布式锁的所有操作(加锁、解锁、续期),都强制路由到「同一个 Redis 主节点」。

  • 具体实现:

    • 锁的 Key 格式固定(如 redisson_lock:{锁名称}),Redission 不修改 Key 的哈希计算逻辑,而是依赖 Redis Cluster 的路由规则:
      同一把锁的 Key 经过 CRC16 计算后,始终映射到同一个哈希槽,进而路由到同一个主节点。
    • 所有客户端操作这把锁时,都会被 Redis Cluster 路由到该主节点,相当于「锁的集中存储」,避免了锁在多个节点分散导致的互斥失效。
  • 举例:

    锁名称为 order_lock,Key 是 redisson_lock:order_lock:

    • 客户端 A 连接 Cluster 节点 1,计算 Key 的槽位为 1234,路由到主节点 3;
    • 客户端 B 连接 Cluster 节点 2,计算同一 Key 的槽位仍为 1234,同样路由到主节点 3;
    • 所有客户端的加锁/解锁操作都在主节点 3 执行,保证了锁的「全局互斥」------ 这是集群环境下锁分布性的基础。

3.4 核心共性总结

所有 Redission 分布式锁的底层设计都围绕以下 3 点:

  1. Redis 数据结构:根据锁的特性选择合适的结构(Hash 用于重入锁、ZSet 用于公平锁/可过期信号量、String 用于信号量);
  2. Lua 脚本:保证加锁、解锁、计数器修改等操作的原子性,避免并发问题;
  3. 看门狗机制:针对互斥锁(可重入锁、公平锁、红锁等),自动续期避免锁提前释放,适配长耗时业务。

3.5 分布式锁选型建议

锁类型 核心场景 特点
可重入锁(ReentrantLock) 大多数分布式互斥场景 性能优、通用性强
公平锁(FairLock) 需保证锁获取顺序(避免饥饿) 性能略低
联锁(MultiLock) 同时锁定多个资源 原子性保证多锁一致性
红锁(RedLock) 高可用场景(金融、核心数据) 部署成本高、可用性极强
读写锁(ReadWriteLock) 读多写少场景 提高读并发效率
信号量(Semaphore) 限流、资源池控制 控制并发数量
闭锁(CountDownLatch) 分布式任务协调 等待多个任务完成

根据业务场景选择合适的锁类型,优先使用「可重入锁」(通用性最强),高可用场景考虑「红锁」,读多写少场景用「读写锁」。

四、高可用部署适配

Redission 高可用部署适配的核心逻辑是:不侵入 Redis 集群原生机制,通过客户端层面的「拓扑感知、故障适配、一致性兜底」三大核心能力,屏蔽 Redis 不同部署模式(主从/哨兵、Cluster、RedLock 等)的底层差异与故障风险,确保分布式锁/服务在节点宕机、拓扑变化时仍稳定可用。

4.1 拓扑动态感知:集群状态的实时雷达

Redission 客户端启动时及运行中,主动获取并刷新 Redis 集群拓扑信息(节点列表、主从关系、槽位映射),确保所有操作都能路由到正确节点。

  • 启动时:连接任意一个节点(哨兵/Cluster 节点),拉取全量拓扑;
  • 运行中:定期刷新(默认 60s,可配置)或被动触发(收到 MOVED/ASK/CLUSTERDOWN 响应时)。

4.2 故障转移适配:主节点宕机后的自动切换

当 Redis 主节点宕机,哨兵触发故障转移(从节点升级为新主),Redission 客户端通过以下流程无感知适配:

  1. 检测主节点连接失败;
  2. 向哨兵查询最新主节点;
  3. 切换连接到新主节点;
  4. 重试之前的锁操作(加锁/续期/解锁)。

4.3 Cluster 集群适配:槽位路由与迁移处理

Cluster 模式下,Redission 核心是「锁 Key 路由集中化」+「槽位迁移适配」:

  • 路由集中化:同一把锁的 Key 经 CRC16 计算后,始终映射到同一个哈希槽,路由到同一个主节点,避免跨槽操作;
  • 槽位迁移:收到 MOVED 响应时,立即刷新槽位映射,后续操作自动路由到新主节点。

4.4 RedLock 极致高可用:多节点投票机制

RedLock 不依赖主从复制,通过「多个独立 Redis 节点投票」实现容错:

  1. 客户端向所有独立节点(推荐 3-5 个)发送加锁请求;
  2. 若超过半数节点加锁成功(3 个需 ≥2 个,5 个需 ≥3 个),则整体加锁成功;
  3. 解锁时向所有节点发送解锁请求,避免残留锁。

Redission 高可用适配的本质是「客户端主导的智能适配」,核心可概括为:

  1. 「感知」:通过拓扑刷新实时掌握 Redis 集群状态;
  2. 「适配」:针对不同部署模式的故障场景(主节点宕机、槽位迁移),自动切换连接、重试操作;
  3. 「保障」:通过路由集中化、Lua 原子性、看门狗续期,确保锁的互斥性和一致性;
  4. 「分级」:从主从+哨兵(平衡成本与可用性)到 RedLock(极致容错),适配不同业务的可用性要求。

通过这套机制,Redission 让业务无需关心 Redis 底层部署细节,即可实现分布式锁/服务的高可用。

相关推荐
IT大白鼠4 天前
Redis 系列 · 第 01 篇——认知入门:Redis 是什么
redis·nosql
彧azz4 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
lbb 小魔仙4 天前
OpenClaw + cpolar 实战:远程 NAS、分享小游戏、RDP,再配置公网 AI 入口
数据库·人工智能·redis·oracle·prometheus
夕除5 天前
redis--018
redis
两锭子5 天前
Redis基础
redis
烟沙九洲5 天前
Redis 缓存穿透、缓存击穿、缓存雪崩
数据库·redis
字节探索5 天前
Redis 只会当缓存用?这 10 大实战场景,让你的系统快到飞起
redis·后端
于樱花森上飞舞5 天前
【Redis】哨兵详解
java·开发语言·数据库·redis
Rain的Java大神之路5 天前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
Wang's Blog5 天前
Java 接入Redis: 通用命令与键管理
java·服务器·redis