安徽省网站建设_网站建设公司_AJAX_seo优化
2026/1/8 7:19:39 网站建设 项目流程

Redis分布式锁详细实现演进与Redisson深度解析

一、Redis分布式锁演进历程

1. 基础版:SETNX + EXPIRE(已废弃,存在严重问题)

第1步:尝试获取锁,返回1表示成功,0表示锁已被占用

SETNX lock:order1第2步:如果上一步成功,设置锁的过期时间为30秒 EXPIRE lock:order30

问题分析:

非原子性:两个命令分开执行,如果在SETNX后客户端崩溃,EXPIRE不会执行,锁永远不会释放,导致死锁

无持有者标识:任何客户端都能释放锁,可能导致误删

无法区分不同客户端:不知道锁被谁持有

2. 推荐版:原子SET(当前主流基础方案)

# 单命令原子操作:获取锁 + 设置过期时间 + 设置持有者标识SET lock:order client_001 NX PX30000

代码详解:

lock:order:锁的key,标识业务类型
client_001:锁的value,通常是客户端唯一标识(如UUID+线程ID)
NX:仅当key不存在时才设置(Not eXists)
PX 30000:设置过期时间为30000毫秒(30秒)

优势:
原子性:单个命令,要么全部成功要么全部失败
避免死锁:自动过期机制保证锁最终会释放
持有者标识:value可用于验证锁的持有者

但仍有问题:
锁续期问题:业务执行时间超过30秒时,锁会提前释放
误释放风险:需要额外逻辑保证只有持有者能释放锁

二、Redisson生产级实现深度解析

1. Redisson入门使用

// 初始化Redisson客户端Configconfig=newConfig();config.useSingleServer().setAddress("redis://127.0.0.1:6379");RedissonClientredisson=Redisson.create(config);// 获取锁对象(非阻塞)RLocklock=redisson.getLock("order_lock");// 尝试加锁(支持等待时间)booleanisLocked=lock.tryLock(10,30,TimeUnit.SECONDS);if(isLocked){try{// 业务逻辑processOrder();}finally{// 必须确保释放锁lock.unlock();}}

2. Redisson核心特性详解

特性1:可重入锁(Reentrant Lock)

// 获取可重入锁RLocklock=redisson.getLock("anyLock");// 第一次加锁lock.lock(10,TimeUnit.SECONDS);// 同一个线程内可以再次加锁(重入)lock.lock(5,TimeUnit.SECONDS);// ... 执行一些操作// 需要解锁两次lock.unlock();lock.unlock();

实现原理:
使用Redis的Hash结构存储锁信息:key: lock_name, field: client_uuid:thread_id, value: 重入次数。
每次重入时,将重入次数加1。
释放锁时,重入次数减1,直到为0才真正删除锁。

特性2:公平锁(Fair Lock)

// 获取公平锁RLockfairLock=redisson.getFairLock("fairLock");// 加锁fairLock.lock();try{// 公平锁保证按照请求顺序获取锁// 先请求的客户端先获取锁}finally{fairLock.unlock();}

实现原理:
客户端请求锁时,在Redis中创建一个顺序节点(sorted set)。
按照节点顺序获取锁,先创建的节点先获取。
使用发布订阅机制通知下一个等待的客户端。
代价:性能比普通锁低,因为需要维护顺序和通知机制。

特性3:联锁(MultiLock)

// 创建多个锁对象RLocklock1=redisson.getLock("lock1");RLocklock2=redisson.getLock("lock2");RLocklock3=redisson.getLock("lock3");// 创建联锁,将多个锁关联在一起RedissonMultiLockmultiLock=newRedissonMultiLock(lock1,lock2,lock3);// 获取联锁:必须所有锁都成功获取才算成功multiLock.lock();try{// 需要同时锁定多个资源时使用// 比如:转账需要同时锁定A账户和B账户transfer(accountA,accountB,amount);}finally{multiLock.unlock();}

使用场景:
分布式事务:需要同时锁定多个资源。
跨资源操作:如同时修改用户表和订单表。
避免死锁:按固定顺序获取多个锁(Redisson内部处理)。

特性4:红锁(RedLock)

// 创建多个独立的Redisson客户端(连接到不同Redis实例)RedissonClientclient1=createClient("redis://node1:6379");RedissonClientclient2=createClient("redis://node2:6379");RedissonClientclient3=createClient("redis://node3:6379");RedissonClientclient4=createClient("redis://node4:6379");RedissonClientclient5=createClient("redis://node5:6379");// 从每个客户端获取锁对象RLocklock1=client1.getLock("lock");RLocklock2=client2.getLock("lock");RLocklock3=client3.getLock("lock");RLocklock4=client4.getLock("lock");RLocklock5=client5.getLock("lock");// 创建红锁RedissonRedLockredLock=newRedissonRedLock(lock1,lock2,lock3,lock4,lock5);// 尝试获取红锁booleanisLocked=redLock.tryLock(1000,30000,TimeUnit.MILLISECONDS);if(isLocked){try{// 业务逻辑}finally{redLock.unlock();}}

红锁算法原理:
1、获取当前时间(毫秒级精度)
2、依次尝试从N个独立的Redis实例获取锁
使用相同的key和value
设置较短的超时时间(远小于锁自动释放时间)
3、计算获取锁的总耗时
当前时间减去步骤1的时间
4、检查是否获取成功
成功条件:从大多数(N/2 + 1)个实例获取锁成功,且总耗时小于锁有效时间
5、如果获取失败,则在所有实例上释放锁

红锁争议:
优点:提高可用性,单个Redis实例故障不影响锁服务
缺点:性能下降,实现复杂,仍有极端情况下的锁安全问题
建议:仅在需要极高可用性且接受复杂性的场景使用

3. Redisson内部实现机制

看门狗(WatchDog)机制

// Redisson加锁时自动启动看门狗线程lock.lock();// 不指定leaseTime,启用看门狗// 指定leaseTime,不启用看门狗lock.lock(10,TimeUnit.SECONDS);// 10秒后自动释放,无看门狗

看门狗工作流程:

1、加锁成功后,启动一个后台线程(看门狗)

2、每隔锁过期时间/3(默认10秒)检查一次

3、如果业务还在执行,则重置锁的过期时间(默认30秒)

4、业务完成后,停止看门狗线程

Lua脚本保证原子性

// Redisson释放锁的Lua脚本Stringscript="if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then "+" return nil;"+"end; "+"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); "+"if (counter > 0) then "+" redis.call('pexpire', KEYS[1], ARGV[2]); "+" return 0; "+"else "+" redis.call('del', KEYS[1]); "+" redis.call('publish', KEYS[2], ARGV[1]); "+" return 1; "+"end; "+"return nil;";

脚本解释:
KEYS[1]:锁的key
KEYS[2]:发布订阅的channel
ARGV[1]:解锁消息
ARGV[2]:锁的过期时间
ARGV[3]:客户端标识(UUID:threadId)
功能:验证持有者 → 重入计数减1 → 判断是否完全释放 → 删除锁并通知等待者

4. 生产环境最佳实践

@ComponentpublicclassOrderService{@AutowiredprivateRedissonClientredisson;publicvoidprocessOrder(StringorderId){StringlockKey="order:lock:"+orderId;RLocklock=redisson.getLock(lockKey);// 尝试获取锁,最多等待5秒,锁持有时间10秒try{booleanacquired=lock.tryLock(5,10,TimeUnit.SECONDS);if(!acquired){thrownewBusinessException("系统繁忙,请稍后重试");}// 获取锁成功,执行业务逻辑handleOrder(orderId);}catch(InterruptedExceptione){Thread.currentThread().interrupt();thrownewBusinessException("处理被中断");}finally{// 只有当前线程持有锁时才释放if(lock.isHeldByCurrentThread()){lock.unlock();}}}// 异步处理,避免长时间占用锁@AsyncpublicCompletableFuture<Void>asyncProcessOrder(StringorderId){returnCompletableFuture.runAsync(()->processOrder(orderId));}}
  1. 性能调优与监控
Redisson配置示例redisson:single-server-config:address:"redis://localhost:6379"connection-pool-size:100# 连接池大小connection-minimum-idle-size:10# 最小空闲连接数idle-connection-timeout:10000# 空闲连接超时时间connect-timeout:10000# 连接超时时间timeout:3000# 操作超时时间retry-attempts:3# 重试次数retry-interval:1500# 重试间隔lock:watchdog-timeout:30000# 看门狗超时时间lock-watch-timeout:10000# 锁监控超时

6. 常见问题与解决方案

总结

Redis分布式锁从基础到生产级的演进,体现了对可靠性、性能、可用性的不断追求:

1、基础版:简单但危险,仅适用于学习和测试

2、原子SET版:满足基本需求,但需自行处理续期和释放

3、Redisson版:生产级方案,提供可重入、公平锁、联锁、红锁等高级特性,内置看门狗和原子性保证

选择建议:
1、大部分场景:使用Redisson普通锁(可重入锁)
2、需要严格顺序:公平锁
3、多资源原子操作:联锁
4、极高可用性需求:红锁(但需权衡复杂性)

黄金法则:永远在finally块中释放锁,并验证当前线程是否持有锁。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询