湖北省建设厅招骋网站,南阳做网站价格,淘宝客网站推广怎么做,滨海专业做网站的公司Redisson 实现分布式读写锁的核心原理是 基于 Redis 的 Lua 脚本原子操作 Pub/Sub 通知机制#xff0c;在保证强一致性的同时实现高效的读并发#xff08;读不阻塞读#xff0c;写阻塞读#xff09;。以下是其核心设计#xff1a;一、核心数据结构Redisson 使用…Redisson 实现分布式读写锁的核心原理是 基于 Redis 的 Lua 脚本原子操作 Pub/Sub 通知机制在保证强一致性的同时实现高效的读并发读不阻塞读写阻塞读。以下是其核心设计
一、核心数据结构Redisson 使用 Redis 的 Hash 结构 存储锁信息Key: {锁名称}如 my_lockHash 字段:mode: 锁模式read/writeUUID:threadId: 持有锁的客户端标识如 c983678b-1421-4c76-8ea0-7f3ab7d9c775:1count: 锁的重入次数支持可重入
二、读锁Read Lock实现原理1. 获取读锁流程
-- 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); -- 重入次数1redis.call(pexpire, KEYS[1], ARGV[1]); -- 刷新超时return nil;
end;
if (redis.call(hexists, KEYS[1], mode) 1) and (redis.call(hget, KEYS[1], mode) read) 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]); -- 存在写锁返回剩余时间需等待关键点只要当前无写锁mode 非 write读锁可直接获取不阻塞其他读锁。多个读锁共享同一个 Hash 结构通过字段区分不同客户端。2. 读锁释放
if (redis.call(hexists, KEYS[1], ARGV[2]) 0) then return nil; end; -- 锁不存在
local counter redis.call(hincrby, KEYS[1], ARGV[2], -1); -- 重入次数-1
if (counter 0) thenredis.call(hdel, KEYS[1], ARGV[2]); -- 移除当前线程的锁
end;
if (redis.call(hlen, KEYS[1]) 1) then -- 只剩 mode 字段无任何锁redis.call(del, KEYS[1]); -- 删除整个 Keyredis.call(publish, KEYS[2], ARGV[1]); -- 发布解锁通知
end;
return 1;
三、写锁Write Lock实现原理1. 获取写锁流程
if (redis.call(exists, KEYS[1]) 0) then -- 无任何锁redis.call(hset, KEYS[1], mode, write); -- 设置为写模式redis.call(hset, KEYS[1], ARGV[2], 1); -- 记录持有者redis.call(pexpire, KEYS[1], ARGV[1]); -- 设置超时return nil;
end;
if (redis.call(hexists, KEYS[1], mode) 1) and (redis.call(hget, KEYS[1], mode) write) then -- 已有写锁if (redis.call(hexists, KEYS[1], ARGV[2]) 1) then -- 当前线程持有写锁redis.call(hincrby, KEYS[1], ARGV[2], 1); -- 重入次数1redis.call(pexpire, KEYS[1], ARGV[1]);return nil;end;
end;
return redis.call(pttl, KEYS[1]); -- 存在读锁或其他写锁返回剩余时间需等待关键点写锁要求绝对互斥必须无任何锁读/写存在才能获取。若存在读锁或其他写锁客户端需等待通过 Pub/Sub 监听解锁通知。2. 写锁释放
if (redis.call(hexists, KEYS[1], ARGV[3]) 0) then return nil; end; -- 锁不存在
local counter redis.call(hincrby, KEYS[1], ARGV[3], -1); -- 重入次数-1
if (counter 0) thenredis.call(hdel, KEYS[1], ARGV[3]); -- 移除持有者
end;
if (redis.call(hlen, KEYS[1]) 1) then -- 只剩 mode 字段redis.call(del, KEYS[1]); -- 删除 Keyredis.call(publish, KEYS[2], ARGV[1]); -- 发布解锁通知
end;
return 1;
四、阻塞等待与通知机制1. 锁竞争时的等待策略当锁获取失败时Redisson 不轮询而是通过 Redis 的 Pub/Sub 订阅锁释放事件
// 伪代码订阅解锁通知
RedisPubSub listener new RedisPubSub() {void onMessage(String channel, String message) {if (message.equals(unlock_msg)) {tryAcquireLock(); // 收到通知后重新尝试获取锁}}
};
redis.subscribe(listener, lock_channel);优势避免频繁轮询 Redis减少网络开销。2. 锁超时与续期看门狗机制Watchdog后台线程每隔 10 秒检查锁是否仍被持有若持有则刷新 TTL默认 30 秒防止业务未完成时锁过期。
if (lockAcquired) {scheduleExpirationRenewal(threadId); // 启动看门狗线程
}
五、公平锁实现Redisson 还提供公平读写锁按请求顺序获取锁使用 Redis List 结构作为请求队列。每个客户端获取锁前在队列尾部追加自己的请求 ID。只有队首的请求有权尝试获取锁避免饥饿问题。
总结Redisson 读写锁的核心优势读读并发通过 Hash 结构叠加读锁计数无写锁时读操作永不阻塞。原子性所有锁操作通过 Lua 脚本在 Redis 单线程中执行无竞态条件。低开销等待基于 Pub/Sub 的事件通知取代轮询。容错性锁超时自动释放 看门狗续期避免死锁。可重入支持同一线程多次加锁通过 count 字段实现。
注实际代码比上述伪代码更复杂含重试机制、异常处理等但核心逻辑一致。建议直接阅读 Redisson 源码 中的 RedissonReadLock 和 RedissonWriteLock 类。