【Java 实战日记】Redisson 分布式锁的公平性实现
·
Redisson 分布式锁的公平性实现原理
在分布式系统中,公平锁确保多个客户端按请求顺序获取锁,避免饥饿现象。Redisson 通过 Redis 的 有序集合(Sorted Set) 和 发布订阅(Pub/Sub) 机制实现公平锁,核心流程如下:
1. 请求排队机制
- 当客户端申请锁时,在 Redis 中创建一个有序集合(如
lock_queue:{lockName}),每个客户端生成唯一标识(如UUID:threadID)并插入集合: $$ \text{score} = \text{当前时间戳} + \text{随机微调值} $$ - 客户端按 score 值升序排列,形成等待队列。
2. 锁获取逻辑
- 客户端检查自身是否位于队列头部:
// 伪代码:检查是否为第一个等待者 if (redis.zrank("lock_queue:myLock", clientId) == 0) { // 尝试获取锁 return tryAcquireLock(); } - 若未轮到当前客户端,通过 Redis 的
subscribe监听前一个节点的释放通知。
3. 锁释放与通知
- 锁释放时删除当前节点,并触发事件:
// 伪代码:释放锁并通知下一节点 redis.zrem("lock_queue:myLock", currentClientId); redis.publish("channel:myLock", "UNLOCK"); - 订阅该频道的下一个客户端收到通知后重试获取锁。
4. 超时与容错
- 客户端设置超时时间(如
waitTime=10s),超时后放弃排队。 - 通过 Lua 脚本保证原子性操作:
-- 原子化检查队列位置和获取锁 if redis.call('zrank', KEYS[1], ARGV[1]) == 0 then return redis.call('set', KEYS[2], ARGV[2], 'PX', ARGV[3]) end return 0
Java 实现示例
import org.redisson.Redisson;
import org.redisson.api.RFairLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class FairLockDemo {
public static void main(String[] args) throws InterruptedException {
// 1. 配置 Redisson 客户端
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
RedissonClient client = Redisson.create(config);
// 2. 获取公平锁实例
RFairLock fairLock = client.getFairLock("orderLock");
// 3. 尝试获取锁(等待20秒,锁持有10秒)
if (fairLock.tryLock(20, 10, TimeUnit.SECONDS)) {
try {
System.out.println("【成功获取锁】执行关键业务...");
Thread.sleep(3000); // 模拟业务处理
} finally {
fairLock.unlock();
System.out.println("【释放锁】");
}
} else {
System.out.println("获取锁超时");
}
client.shutdown();
}
}
关键参数说明:
tryLock(waitTime, leaseTime, unit):waitTime:排队等待的最长时间leaseTime:锁自动释放时间(防止死锁)
- 公平锁特性:
- 按请求顺序分配锁
- 避免优先级反转
- 支持可重入(同一线程可多次加锁)
性能优化建议
- 减少网络开销:
- 使用 Lua 脚本合并多个 Redis 操作
- 超时策略:
- 根据业务场景调整
waitTime和leaseTime
- 根据业务场景调整
- 监控队列长度:
- 定期检查有序集合大小,避免无限堆积
long queueSize = fairLock.size(); // 获取等待队列长度
注:公平锁比非公平锁性能略低(约 10%~15%),但适用于需要严格顺序的业务场景(如订单处理、资源调度)。
更多推荐
所有评论(0)