Redission实现的分布式锁的可重入性

Redisson 分布式锁在 Redis 中存储可重入状态所使用的 Hash 结构,并通过示例说明。

核心数据结构

  • Key: 锁的名称。例如:"myLock"
  • 数据类型: Hash (Redis HSET / HGET / HINCRBY 操作的对象)。
  • Hash Field (字段名): 客户端唯一标识符 。格式通常为:UUID:threadId
    • UUID: 生成 Redisson 客户端实例时创建的一个全局唯一 ID(一个 JVM 进程一个)。
    • threadId: 当前请求锁的线程 ID(Java 中的 Thread.currentThread().getId())。
    • 作用: 精确标识是哪个 JVM 进程中的哪个线程持有锁。这是实现可重入的基础,同一个线程多次获取锁时,Field 相同。
  • Hash Value (字段值): 一个整数 ,表示该线程对这把锁的 重入次数
    • 当线程第一次成功获取锁时,这个值被设置为 1
    • 当同一个线程再次成功获取锁(重入)时,这个值会递增(+1)。
    • 当线程释放锁时,这个值会递减(-1)。
    • 只有当这个值减到 0 时,才表示该线程已经完全释放了这把锁,此时 Redis 会删除这个 Hash Key。

示例场景

假设:

  1. 锁名称 (Key): "order_lock:1001" (假设为处理订单 ID 1001 的锁)
  2. 客户端 A 的 UUID: "550e8400-e29b-41d4-a716-446655440000"
  3. 客户端 A 中线程 ID: 31
场景 1: 线程 31 第一次获取锁成功
  • Redis 命令模拟 (Lua 脚本执行后的效果):

    bash 复制代码
    HSET order_lock:1001 550e8400-e29b-41d4-a716-446655440000:31 1
    PEXPIRE order_lock:1001 30000 # 设置 30 秒过期时间
  • Redis 中存储的数据:

    复制代码
    Key: "order_lock:1001"
      Type: hash
      Field: "550e8400-e29b-41d4-a716-446655440000:31" -> Value: 1 (整数)
      TTL: ~30000ms
  • 解释: 线程 31 首次获取锁,重入次数为 1

场景 2: 同一个线程 31 在锁内再次获取锁成功 (重入)
  • 假设线程 31 在持有 order_lock:1001 的代码块内,又调用了另一个需要获取同一把锁的方法。

  • Redis 命令模拟 (Lua 脚本执行后的效果):

    bash 复制代码
    HINCRBY order_lock:1001 550e8400-e29b-41d4-a716-446655440000:31 1 # 值从1变成2
    PEXPIRE order_lock:1001 30000 # 重置过期时间
  • Redis 中存储的数据:

    复制代码
    Key: "order_lock:1001"
      Type: hash
      Field: "550e8400-e29b-41d4-a716-446655440000:31" -> Value: 2 (整数)
      TTL: ~30000ms (被重置)
  • 解释: 同一个线程 31 重入锁,重入次数递增到 2。锁的过期时间被重置(看门狗或续期逻辑)。

场景 3: 线程 31 第一次释放锁 (重入锁)
  • 线程 31 执行到外层锁的 unlock()

  • Redis 命令模拟 (Lua 脚本执行后的效果):

    bash 复制代码
    HINCRBY order_lock:1001 550e8400-e29b-41d4-a716-446655440000:31 -1 # 值从2变成1
    PEXPIRE order_lock:1001 30000 # 重置过期时间 (因为计数 > 0)
  • Redis 中存储的数据:

    复制代码
    Key: "order_lock:1001"
      Type: hash
      Field: "550e8400-e29b-41d4-a716-446655440000:31" -> Value: 1 (整数)
      TTL: ~30000ms
  • 解释: 线程 31 释放了一次锁(对应第一次获取),重入次数减为 1。锁仍然被该线程持有(计数 > 0),所以 Key 没有被删除,并且过期时间被重置。

场景 4: 线程 31 第二次释放锁 (完全释放)
  • 线程 31 执行到内层锁的 unlock()

  • Redis 命令模拟 (Lua 脚本执行后的效果):

    bash 复制代码
    HINCRBY order_lock:1001 550e8400-e29b-41d4-a716-446655440000:31 -1 # 值从1变成0
    DEL order_lock:1001 # 因为计数减到0,删除Key
    PUBLISH redisson_lock__channel:{order_lock:1001} ... # 发布解锁消息通知等待者
  • Redis 中存储的数据:

    复制代码
    Key: "order_lock:1001" -> 已被删除
  • 解释: 线程 31 释放了最后一次持有的锁(对应第二次获取),重入次数减为 0。此时 Redis 会删除整个 Key order_lock:1001,并通过 PUBLISH 命令通知所有正在等待这把锁的其他客户端。

关键点总结

  1. 结构: 一个锁 Key (String) 对应一个 Hash 数据结构。
  2. 标识: Hash 的 Field<ClientUUID>:<ThreadID>,唯一标识持有锁的客户端和线程。
  3. 计数: Hash 的 Value 是一个 整数 ,记录该线程对这把锁的 重入次数
  4. 原子操作: 加锁 (HINCRBY ... 1 / HSET ... 1)、解锁 (HINCRBY ... -1)、检查持有者 (HEXISTS)、删除锁 (DEL) 等关键操作都封装在 Lua 脚本中执行,保证原子性。
  5. 可重入性: 同一个线程多次获取锁时,操作的是同一个 Hash Field,对其 Value 进行递增;释放时递减,直到为 0 才真正释放锁。这完美实现了可重入锁的语义。

通过这种 Hash 结构的设计,Redisson 高效、清晰地实现了分布式环境下锁的可重入性管理。

相关推荐
嘻哈baby3 小时前
Redis高可用部署与集群管理实战
数据库·redis·bootstrap
Java爱好狂.5 小时前
Java面试Redis核心知识点整理!
java·数据库·redis·分布式锁·java面试·后端开发·java八股文
阿杆6 小时前
如何在 Spring Boot 中接入 Amazon ElastiCache
java·数据库·redis
此生只爱蛋11 小时前
【Redis】String 字符串
java·数据库·redis
青云交11 小时前
Java 大视界 -- 基于 Java+Flink 构建实时电商交易风控系统实战(436)
java·redis·flink·规则引擎·drools·实时风控·电商交易
破烂pan11 小时前
Python 整合 Redis 哨兵(Sentinel)与集群(Cluster)实战指南
redis·python·sentinel
SoleMotive.12 小时前
redis和mysql有什么区别,以及redis和mysql都有什么缺点,以及什么地方redis不如mysql?
数据库·redis·mysql
锥锋骚年13 小时前
golang 开发 Redis与Memory统一接口方案
开发语言·redis·golang
bafuka13 小时前
别再手撸热点缓存了:一个注解搞定Redis热点问题(已开源)
redis
程可爱13 小时前
详解Redis的五种基本数据类型(String、List、Hash、Set、ZSet)
redis