引言
大家好啊,今天我们来讲讲分布式锁有哪几种实现方式。一般我们在面试中,通常就只会回答redis使用setnx来做分布式锁,但是面试官如果扩展来问,分布式锁还有哪几种实现方式?估计大部分人都是一脸懵,就算想起其他的也得一些时间才能想起,更不用说在面试这种紧张刺激的场景下了🤓。那么我们今天一起盘点一下分布式锁到底还有什么实现方式,让面试官对我们刮目相看(展现我们的技术宽度)。
基于Redis的分布式锁
原理:利用 Redis 的 SET key value NX PX 命令(原子性)实现加锁。
NX:仅当 key 不存在时才设置(保证互斥)PX:设置过期时间(防止死锁)
优点:性能高、延迟低,实现简单
缺点:Redis 主从架构下存在主从切换导致锁丢失的风险(非强一致性),需要处理锁续期、可重入等问题
Redisson 实现(强推)
Redisson 提供了完整的分布式锁实现:
- Watchdog 自动续期(内置)
- 可重入
- 公平锁 / 读写锁
- RedLock(多节点)
一般项目直接用这个就够了
基于关系型数据库的分布式锁
基于关系型数据库(MySQL,PostgreSQL)也可以做分布式锁,下面有几种常见方案:
方式一:唯一索引 + 插入记录
- 尝试插入一条带唯一键的记录,成功则获得锁,失败则等待
- 释放锁时删除记录
方式二:乐观锁(版本号)
update本身就是行级锁,在同一时间只能有一个连接尝试执行这行数据的语句。 这里不加锁,由数据库尝试执行这条语句的时候去检验是否有其他事务修改过,如果被修改,则当前操作失败,由业务层决定是否重试。
- 适用于更新场景,通过
UPDATE ... WHERE version = old_version实现
详细的更新语句如下:
ps:只能保护单条记录的更新,不能用于跨表/复杂业务逻辑
UPDATE table_name SET field1 = new_value, version = version + 1
WHERE id = ? AND version = old_version;
方式三:悲观锁(SELECT FOR UPDATE)
- 在事务中使用
SELECT ... FOR UPDATE锁住某行
优点:
- 无需引入额外中间件
- 熟悉度高
缺点:
- 性能较差(尤其高并发下)
- 数据库成为瓶颈
- 需处理死锁、超时等问题
基于ZooKeeper的分布式锁
原理:利用 ZooKeeper 的临时顺序节点(ephemeral sequential znode)实现。
- 所有客户端在同一个父节点下创建临时顺序节点
- 只有序号最小的节点获得锁,其他节点监听前一个节点的删除事件
优点:
- 强一致性(ZooKeeper 的 ZAB 协议保证)
- 支持公平锁、可重入锁
- 客户端宕机自动释放锁(临时节点)
缺点:
- 性能不如 Redis(写操作需多数派确认)
- 运维复杂度高
基于Etcd的分布式锁
etcd 分布式锁的核心是:
谁成功在指定 key 上创建了带租约的唯一记录,谁就获得锁;其他竞争者监听该 key,一旦释放(过期或主动删除),就尝试抢锁。
这类似于“抢占 + 监听”模式
原理:利用 Lease(租约)和 Compare-and-Swap(CAS)机制。
- 通过
txn事务 +lease实现原子加锁 - 锁与租约绑定,租约到期自动释放
优点:
- 强一致性(Raft 协议)
- 支持 TTL、Watch 机制
- 可通过顺序前缀(如
lock/00001)实现 FIFO 队列式锁 - 云原生生态友好(Kubernetes 使用 etcd)
缺点:
- 相对小众,生态工具不如 Redis/ZooKeeper 成熟
基于分布式协调 / raft 本身的锁
比如:consul sessions/kv,raft log 写入 CAS
这个一般在微服务体系中内置
总结:最佳实践建议
- 高并发、性能敏感 → Redis + Redisson
- 强一致性、可靠性优先 → ZooKeeper 或 Etcd
- 避免重复造轮子 → 优先使用成熟客户端(如 Redisson)
- 注意锁的超时、可重入、死锁、续期等问题