后端面试必备:Redisson分布式锁原理详解

Redis面试题 - 说说Redisson分布式锁的原理?

回答重点

Redisson是基于Redis实现的分布式锁,实际上是使用Redis的原子操作来确保多线程、多进程或多节点系统中,只有一个线程能获得锁,避免并发操作导致的数据不一致问题。

1 锁的获取:

  • Redisson 使用 Lua 脚本,利用exists+hexists+hincrby命令来保证只有一个线程能成 功设置键(表示获得锁)。
  • 同时,Redisson会通过pexpire命令为锁设置过期时间,防止因宕机等原因导致锁无法释放(即死锁问题)。

2 锁的续期:

  • 为了防止锁在持有过程中过期导致其他线程抢占锁,Redisson实现了锁自动续期的功能。持有
    锁的线程会定期续期,即更新锁的过期时间,确保任务没有完成时锁不会失效。

3 锁的释放:

  • 锁释放时,Redisson也是通过Lua脚本保证释放操作的原子性。利用hexists+de1确保只有持有锁的线程才能释放锁,防止误释放锁的情况。
  • Lua脚本同时利用publish命令,广播唤醒其它等待的线程。

4 可重入锁:

  • Redisson支持可重入锁,持有锁的线程可以多次获取同一把锁而不会被阻塞。具体是利用 Redis中的哈希结构,哈希中的key为线程ID,如果重入则value+1,如果释放则value 1减到0说明锁被释放了,则del锁。

一、分布式锁概述

在分布式系统中,当多个进程或线程需要访问共享资源时,为了保证数据一致性,我们需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁。

分布式锁需要满足以下基本要求:

  • 互斥性:同一时刻只有一个客户端能持有锁
  • 避免死锁:即使持有锁的客户端崩溃,锁也能被释放
  • 容错性:只要大部分Redis节点正常运行,客户端就能获取和释放锁

二、Redisson分布式锁核心原理

Redisson实现分布式锁主要依赖于Redis的以下特性:

  • Lua脚本(保证原子性)
  • 发布/订阅机制
  • Hash数据结构

1. 加锁流程

客户端 Redis服务器 执行Lua脚本尝试加锁 加锁成功,返回null 返回锁剩余生存时间 订阅锁释放频道 再次尝试加锁(自旋) alt [锁未被持有] [锁已被持有] 客户端 Redis服务器

加锁的核心Lua脚本逻辑:

if (redis.call('exists', KEYS[1]) == 0) then 
    redis.call('hset', KEYS[1], ARGV[2], 1); 
    redis.call('pexpire', KEYS[1], ARGV[1]); 
    return nil; 
end; 
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 nil; 
end; 
return redis.call('pttl', KEYS[1]);

参数说明:

  • KEYS[1]:锁的名称
  • ARGV[1]:锁的生存时间(毫秒)
  • ARGV[2]:客户端唯一标识(UUID:threadId)

2. 可重入锁实现

Redisson通过Redis的Hash结构实现可重入锁:

  • Hash的field存储客户端唯一标识(UUID:threadId)
  • Hash的value存储重入次数
锁名称
Hash结构
UUID:threadId1: 重入次数
UUID:threadId2: 重入次数

3. 锁续期机制(WatchDog)

为了防止业务执行时间超过锁过期时间,Redisson提供了WatchDog机制自动续期:

客户端 WatchDog线程 Redis 启动WatchDog 延长锁过期时间(默认30秒) loop [每隔10秒检查一次] 业务完成,停止WatchDog 客户端 WatchDog线程 Redis

三、Redisson分布式锁高级特性

1. 公平锁实现

Redisson公平锁通过Redis的List和ZSet结构实现排队机制:

等待队列
线程1
线程2
线程3
超时队列
线程1超时时间
线程2超时时间

2. 红锁(RedLock)算法

Redisson实现了Redis官方推荐的红锁算法,用于提高分布式锁的可靠性:

+----------------+     +-----------------+
|    客户端       |     |   Redis实例1    |
+--------+-------+     +--------+--------+
         |                      |
         | 尝试获取锁            |
         +--------------------->|
         |                      |
         |                      +---+
         |                          |
         |                          v
         |                    +-----+------+
         |                    | 成功/失败  |
         |                    +-----+------+
         |                          |
         |                      +---+
         |                      |
         v                      v
+--------+-------+     +--------+--------+
|    客户端       |     |   Redis实例2    |
+--------+-------+     +--------+--------+
         |                      |
         | 尝试获取锁            |
         +--------------------->|
         |                      |
         |                      +---+
         |                          |
         |                          v
         |                    +-----+------+
         |                    | 成功/失败  |
         |                    +-----+------+
         |                          |
         |                      +---+
         |                      |
         v                      v
+--------+-------+     +--------+--------+
|    客户端       |     |   Redis实例3    |
+--------+-------+     +--------+--------+
         |                      |
         | 尝试获取锁            |
         +--------------------->|
         |                      |
         |                      +---+
         |                          |
         |                          v
         |                    +-----+------+
         |                    | 成功/失败  |
         |                    +-----+------+
         |                          |
         |                      +---+
         |                      |
         v                      v
+--------+-------+     +--------+--------+
|    客户端       |     |   Redis实例4    |
+--------+-------+     +--------+--------+
         |                      |
         | 尝试获取锁            |
         +--------------------->|
         |                      |
         |                      +---+
         |                          |
         |                          v
         |                    +-----+------+
         |                    | 成功/失败  |
         |                    +-----+------+
         |                          |
         |                      +---+
         |                      |
         v                      v
+--------+-------+     +--------+--------+
|    客户端       |     |   Redis实例5    |
+--------+-------+     +--------+--------+
         |                      |
         | 尝试获取锁            |
         +--------------------->|
         |                      |
         |                      +---+
         |                          |
         |                          v
         |                    +-----+------+
         |                    | 成功/失败  |
         |                    +-----+------+
         |                          |
         |                      +---+
         |                      |
         v                      v
         +----------------------+
         |                      |
         | 统计成功数量          |
         | (N/2+1个成功)         |
         |                      |
         v                      v
+--------+-------+     +--------+--------+
|  获取锁成功     |     |  获取锁失败     |
+----------------+     +----------------+

红锁算法步骤:

  1. 获取当前时间
  2. 依次尝试从多个独立的Redis实例获取锁
  3. 计算获取锁消耗的总时间
  4. 当且仅当从多数节点获取锁成功,且总耗时小于锁有效时间时,锁获取成功

四、Redisson分布式锁最佳实践

  1. 锁命名规范:使用业务相关的有意义名称,如order:lock:123表示订单ID为123的锁

  2. 合理设置超时时间

// 尝试获取锁,最多等待100秒,上锁以后10秒自动解锁
RLock lock = redisson.getLock("myLock");
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
  1. 避免嵌套锁:尽量减少锁的嵌套层级,防止死锁

  2. 锁粒度控制:尽量使用细粒度锁,提高并发性能

五、常见问题解答

Q: Redisson分布式锁与SETNX实现的锁有什么区别?

A: Redisson相比简单的SETNX实现有以下优势:

  1. 可重入支持
  2. 自动续期机制
  3. 更完善的超时处理
  4. 提供公平锁、联锁等高级特性
  5. 更完善的异常处理

Q: WatchDog机制会不会导致锁永远不释放?

A: 不会,WatchDog只会在客户端正常工作时续期,如果客户端崩溃,WatchDog线程也会终止,锁最终会因过期而自动释放。

Q: 为什么推荐使用Redisson而不是自己实现分布式锁?

A: 自己实现分布式锁需要考虑:

  • 原子性保证
  • 锁续期问题
  • 网络分区处理
  • 客户端崩溃恢复
  • 可重入性
  • 公平性等

Redisson经过多年生产验证,解决了上述所有问题,比自己实现更可靠。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值