分布式锁基本原理和实现方式对比

一、前言:为什么单机锁在分布式系统中失效?

在单体应用中,我们常用 synchronizedReentrantLock 保证线程安全。

但当系统拆分为多个服务实例(如 10 台服务器同时运行订单服务),本地锁无法跨 JVM 生效

此时,必须引入 分布式锁(Distributed Lock) ------ 一种跨进程、跨机器的互斥机制。

本文将带你深入理解分布式锁的核心要求,并对比 Redis、ZooKeeper、数据库 三种主流实现方案的优劣。


二、分布式锁的三大核心要求

一个合格的分布式锁,必须满足:

✅ 1. 互斥性(Mutual Exclusion)

  • 同一时刻,只能有一个客户端持有锁

✅ 2. 避免死锁(Deadlock Free)

  • 即使持有锁的节点宕机,锁也能自动释放(通过超时机制)

✅ 3. 高可用 & 容错性

  • 锁服务本身不能成为单点故障

⚠️ 额外加分项:可重入性公平性高性能


三、实现方式一:基于数据库(最简单,但性能差)

原理:

利用数据库 唯一索引排他锁(FOR UPDATE) 实现互斥。

方案 A:唯一索引
sql 复制代码
-- 创建锁表
CREATE TABLE distributed_lock (
    lock_name VARCHAR(64) PRIMARY KEY,
    expire_time BIGINT
);

-- 获取锁:插入成功即获得锁
INSERT INTO distributed_lock (lock_name, expire_time) 
VALUES ('order_lock', 1712345678);
-- 失败则抛出唯一索引冲突
方案 B:SELECT FOR UPDATE
java 复制代码
// 事务中执行
SELECT * FROM distributed_lock WHERE lock_name = 'order_lock' FOR UPDATE;
// 执行业务逻辑...

✅ 优点:

  • 实现简单,无需额外中间件
  • 强一致性(依赖 DB ACID)

❌ 缺点:

  • 性能极差:DB 成为瓶颈
  • 无自动过期:需额外定时任务清理
  • 主从切换可能丢锁

📌 适用场景:低并发、已有 DB 且无 Redis/ZK 的遗留系统


四、实现方式二:基于 Redis(高性能,但需注意细节)

基础实现(错误示范!):

bash 复制代码
# ❌ 错误:非原子操作
GET lock_key
# 若不存在
SET lock_key "client_1"
EXPIRE lock_key 30

问题SETEXPIRE 不是原子的,若服务在 SET 后 crash,锁永不过期!

✅ 正确实现:使用 SET key value NX EX seconds

bash 复制代码
# 原子获取锁(Redis 2.6.12+)
SET lock_key "client_1" NX EX 30
  • NX:仅当 key 不存在时设置
  • EX 30:30 秒后自动过期

释放锁(需校验 owner):

Lua 复制代码
-- unlock.lua
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("DEL", KEYS[1])
else
    return 0
end

防止 A 释放了 B 的锁!

高级方案:RedLock(Redis 官方推荐)

  • N 个独立 Redis 节点 请求锁
  • 超过半数成功 + 总耗时 < 锁有效期 → 获取成功

✅ 优点:

  • 性能极高(微秒级)
  • 自动过期防死锁
  • 客户端实现简单

❌ 缺点:

  • CP or AP?:Redis 主从异步复制,主挂可能导致锁丢失(不满足强一致性)
  • RedLock 实现复杂,争议较大(Martin Kleppmann 曾质疑其安全性)

📌 适用场景:高并发、允许短暂不一致的业务(如秒杀、缓存重建)


五、实现方式三:基于 ZooKeeper(强一致,但性能较低)

原理:

利用 ZK 的 临时顺序节点(Ephemeral Sequential Node) 实现公平锁。

流程:

  1. 所有客户端在 /locks 下创建临时顺序节点(如 /locks/lock-0000000001
  2. 客户端检查自己是否是最小序号节点
    • 是 → 获得锁
    • 否 → 监听前一个节点的删除事件(Watcher)
  3. 业务执行完,删除自身节点(或会话断开自动删除)

✅ 优点:

  • 强一致性(ZAB 协议保证)
  • 天然支持公平锁 & 可重入
  • 无死锁风险(临时节点随会话销毁)

❌ 缺点:

  • 性能较低:频繁创建/删除节点 + 网络往返
  • 运维复杂:需维护 ZK 集群
  • 羊群效应:大量 Watcher 可能引发性能抖动(可通过"只监听前驱"优化)

📌 适用场景:对一致性要求极高、并发不极端的场景(如配置管理、选主)


六、三大方案对比总结

维度 数据库 Redis ZooKeeper
实现复杂度 ⭐⭐ ⭐⭐⭐
性能 ❌ 极低 ✅ 极高 ⚠️ 中等
一致性 ✅ 强(ACID) ⚠️ 最终一致(主从异步) ✅ 强(ZAB)
自动过期 ❌ 需手动 ✅ 支持 ✅ 临时节点
公平性 ✅ 支持
高可用 依赖 DB HA Redis Cluster ZK 集群(≥3 节点)
典型场景 低并发遗留系统 秒杀、缓存重建 分布式协调、选主

💡 一句话选型建议

  • 要性能 → 选 Redis
  • 要强一致 → 选 ZooKeeper
  • 已有 DB 且并发低 → 用数据库

七、避坑指南:常见错误实践

❌ 错误 1:Redis 锁不设超时

后果 :服务 crash 后锁永远不释放
正解必须用 EX 设置 TTL

❌ 错误 2:释放锁时不校验 value

风险 :A 释放了 B 的锁,导致并发安全问题
正解Lua 脚本比对 owner

❌ 错误 3:ZooKeeper 使用永久节点

后果 :客户端宕机后锁残留
正解必须用临时节点(Ephemeral)


八、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
qq_12498707535 小时前
基于Hadoop的信贷风险评估的数据可视化分析与预测系统的设计与实现(源码+论文+部署+安装)
大数据·人工智能·hadoop·分布式·信息可视化·毕业设计·计算机毕业设计
Coder_Boy_7 小时前
基于Spring AI的分布式在线考试系统-事件处理架构实现方案
人工智能·spring boot·分布式·spring
袁煦丞 cpolar内网穿透实验室8 小时前
远程调试内网 Kafka 不再求运维!cpolar 内网穿透实验室第 791 个成功挑战
运维·分布式·kafka·远程工作·内网穿透·cpolar
人间打气筒(Ada)8 小时前
GlusterFS实现KVM高可用及热迁移
分布式·虚拟化·kvm·高可用·glusterfs·热迁移
xu_yule9 小时前
Redis存储(15)Redis的应用_分布式锁_Lua脚本/Redlock算法
数据库·redis·分布式
難釋懷13 小时前
分布式锁的原子性问题
分布式
ai_xiaogui14 小时前
【开源前瞻】从“咸鱼”到“超级个体”:谈谈 Panelai 分布式子服务器管理系统的设计架构与 UI 演进
服务器·分布式·架构·分布式架构·panelai·开源面板·ai工具开发
凯子坚持 c14 小时前
如何基于 CANN 原生能力,构建一个支持 QoS 感知的 LLM 推理调度器
分布式
飞升不如收破烂~14 小时前
Redis 分布式锁+接口幂等性使用+当下流行的限流方案「落地实操」+用户连续点击两下按钮的解决方案自用总结
数据库·redis·分布式