分布式锁一般都怎样实现?
- 标签:后端、系统设计、场景题
🏛 业务架构师
四种实现按一致性强度与运维成本排,我的选型顺序是从轻到重:
- Redis(SETNX + 过期 + 唯一值 + Lua 释放):性能最好、最普及。弱点:主从切换瞬间可能丢锁(锁还没同步到从库),续期问题需 Redisson 看门狗。适合"锁失效可容忍"的场景——防重复提交、任务防重跑,配合幂等兜底;
- 数据库乐观锁(version 字段):最简单,不加锁、update 影响行数判断成败。没有锁组件依赖,DB 就是事实源——冲突少的业务首选;高冲突时重试风暴,需配合重试上限与退避;
- 数据库悲观锁(select for update):强一致、简单直接,资金类操作的经典选择——事务持有锁,DB 崩溃锁自动释放无"死锁残留"。吞吐低(排队在 DB),留给低频高价值操作(账户划转、库存终扣);
- ZooKeeper/etcd(临时顺序节点 + watch):共识协议保证强一致,会话断开锁自动释放(无过期误伤问题)。代价:运维 ZK 集群、性能低于 Redis——"锁失效即资损"且频率不极端的场景。
我的决策树:
- 能不用锁就不用——队列串行化、幂等设计先上(锁是优化不是正确性来源);
- 冲突少 → DB 乐观锁;
- 高频抢锁但失效可容忍 → Redis(Redisson)+ 幂等兜底;
- 强一致 + 低频 → DB 悲观锁;
- 强一致 + 中频 + 已有 ZK → ZK 锁。
红线:无论哪种锁,业务正确性必须有独立防线(唯一约束/状态机)——锁挡并发,约束兜住锁失效的缝隙。见过"锁没拿到但代码继续跑"的实现,锁成了心理安慰。
🏗 系统架构师
分布式锁的三条主流路线,我按"一致性强度 vs 性能/运维成本"对比:
1. Redis(AP 倾向,性能最高):
- 实现:
SET key value NX PX ttl+ Lua 原子释放;生产用 Redisson(可重入、看门狗续期、pub/sub 唤醒); - 优点:TPS 高(10 万级)、延迟低、部署普遍;
- 风险:主从异步复制——锁未同步主宕机,新主上锁丢失,互斥被破坏(极端场景);RedLock 多节点多数派争议大,已不主流;
- 适用:防重复执行、防并发写的"性能锁"。
2. ZooKeeper(CP,最稳):
- 实现:创建
/lock下的临时顺序节点,最小序号获锁,其余 watch 前一个节点(避免惊群);会话断开临时节点自动删除,不会死锁; - 优点:强一致(ZAB),无 Redis 的丢锁问题;
- 缺点:性能低一个量级(写要走 quorum),运维 ZK 集群成本;Curator 的 InterProcessMutex 现成可用;
- 适用:主备选举、准确性优先的核心调度。
3. 数据库(最朴素,强一致兜底):
- 唯一约束插入(insert 冲突即抢锁失败)或
select ... for update行锁,或乐观锁版本号; - 优点:无新组件、事务语义天然;缺点:性能差(单点 DB)、无自动过期(要配超时任务清理);
- 适用:低频关键互斥(定时任务防重跑)、以及正确性兜底(Redis 锁之后 DB 唯一约束再防一道)。
我的架构实践:默认 Redisson 挡量、DB 约束兜正确性;真正不能错的选主/审计用 ZK 或 etcd。锁只是优化手段,业务幂等才是最终防线。
💻 全栈开发专家
三种主流实现,我按"正确性 vs 性能"谱系讲:
1. 数据库实现(简单但性能差)
sql
-- 方式 A:唯一索引,插入即抢锁
INSERT INTO dist_lock (lock_key, owner, expire_at) VALUES ('order:1', 'uuid-1', NOW()+10s);
-- 释放/恢复:定期 DELETE 过期的记录依赖唯一约束,强一致,但每秒几千次抢锁 DB 扛不住。适合低频场景(定时任务防并发)。
2. Redis(性能最好,主流选择)
bash
SET lock:order:1 $uuid NX EX 10配套:唯一 value + Lua 校验删除防误删、Redisson 看门狗续期防过期、可重入。弱点:主从切换瞬间可能两个客户端同时持锁(异步复制丢锁),CAP 里偏 AP——适合"锁偶尔失效业务能兜底"的场景。
3. ZK/etcd(正确性最高)
- ZK:创建临时顺序节点,最小序号者持锁,其他人 watch 前一个节点——无续期问题(session 断开节点自动删除,锁自动释放),无 Redis 的切换丢锁问题;
- etcd:lease(租约)+ 事务写,同 ZK 语义。
代价:写要过半数节点确认,性能低于 Redis;运维复杂度高。
选型口诀:高并发抢锁、允许极小概率失效(有唯一索引兜底)→ Redisson;强正确性(只能一个人操作,错了出事故)→ ZK/etcd;低频 → DB 唯一索引就够。
必须补的半句:任何分布式锁都要回答"锁失效了怎么办"——重入/续期、 fencing token、DB 层约束,这三个词说出来这题就完整了。