c# 分布式锁的实现 c# Redis 和 Zookeeper 实现分布式锁的区别

来源:这里教程网 时间:2026-02-21 17:36:07 作者:

Redis 分布式锁为什么容易丢锁

Redis 实现分布式锁最常见的是用

SET key value NX PX timeout
,但仅靠这一条命令无法应对所有异常场景。比如客户端 A 拿到锁后,在业务执行中发生 GC 停顿或网络抖动,导致 Redis 中的 key 过期被自动删除;此时客户端 B 顺利加锁,A 恢复后仍按原逻辑执行释放操作——它会误删 B 的锁。

必须用唯一标识(如 UUID)作为
value
,释放锁时通过 Lua 脚本比对
value
DEL
,避免误删
锁超时时间不能拍脑袋设,需覆盖「最长可能执行时间 + 网络毛刺余量」,否则频繁过期引发冲突 单节点 Redis 故障会导致锁服务不可用;用 Redis Sentinel 或 Cluster 时,主从异步复制可能导致锁在主节点写入成功、从节点未同步就故障切换,造成重复加锁

Zookeeper 分布式锁依赖临时顺序节点

Zookeeper 的分布式锁本质是利用

EPHEMERAL_SEQUENTIAL
节点和 Watch 机制。每个客户端在指定路径(如
/lock
)下创建临时顺序子节点,然后检查自己是否是序号最小的节点:如果是,获得锁;否则监听前一个节点的删除事件。

客户端断连后,ZK 自动删除其创建的临时节点,无需超时机制,天然防死锁 ZK 的强一致性(ZAB 协议)保证所有客户端看到的节点状态严格一致,不存在 Redis 主从不一致导致的锁冲突 但 ZK 集群至少需 3 节点,运维成本高;频繁争锁会产生大量 Watch 事件和节点创建/删除,压测时易触发连接数或 ZNode 数量限制

C# 客户端实现的关键差异点

.NET 生态中,

StackExchange.Redis
Curator
(需通过 IKVM 或 .NET Core 兼容层)或
ZooKeeperNetEx
是主流选择。二者 API 抽象层级完全不同:

Redis 锁通常封装为
TryAcquire(string key, string value, int expiryMs)
Release(string key, string value)
,核心是原子 Lua 脚本调用
ZK 锁更倾向基于租约(lease)模型,如
InterProcessMutex
类,内部处理重试、Watch 回调、会话重建等,使用者只需
mutex.Acquire()
/
mutex.Release()
Redis 锁失败后一般立即返回 false;ZK 锁默认阻塞等待,可通过
Acquire(timeout)
控制,但超时判定依赖 ZK 会话超时(
sessionTimeout
),不是应用层计时
// Redis 锁释放 Lua 脚本示例(C# 中通过 Eval 执行)
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("DEL", KEYS[1])
else
    return 0
end

选型时真正该看的不是语言而是 SLA

如果业务能容忍秒级锁失效(如定时任务去重),Redis 更轻量、吞吐高;如果涉及资金、库存等强一致性场景,且已有 ZK 基础设施,ZK 的可靠性更可预期。

别忽略部署环境:K8s 里跑 ZK 集群的 Pod 重启、滚动更新、网络分区都会影响会话稳定性;而 Redis 在云上托管服务(如 Azure Cache for Redis)已内置高可用,但得确认其是否支持

WAIT
命令保障写入多数节点。

最容易被跳过的点是锁的粒度——用同一个

key
锁住所有订单 vs 按
order_id
分锁,后者才能真正并发,前者只是串行化。

相关推荐

热文推荐