除了 Redis 实现分布式锁,还有哪些方案可以实现分布式锁?各有什么优缺点?
精炼回答
除了 Redis,常见的分布式锁实现方案还包括数据库、ZooKeeper、etcd 和 Consul。
- 数据库:无需额外引入中间件,适合低频、低竞争场景,但高并发下性能和连接资源压力较大。
- Redis:延迟低、吞吐高、生态成熟,适合高并发场景,但主从故障切换、锁过期等情况下需要额外考虑锁安全问题。
- ZooKeeper:基于临时顺序节点和 Watch 机制实现,适合需要可靠协调、公平排队和 Leader Election 的场景,但部署和运维成本较高。
- etcd:基于 Lease、Revision、Transaction 和 Lock API 实现,强一致、云原生生态完善,非常适合 Kubernetes 等基础设施场景。
- Consul:基于 KV 和 Session 实现,适合已经使用 Consul 服务发现体系的系统,但不应把 WAN Gossip 理解成跨数据中心同步全局锁的机制。
实际开发中不存在绝对“最好”的分布式锁方案,应根据一致性要求、性能、业务容错能力、已有基础设施和运维复杂度综合选择。
扩展分析
分布式锁用于解决多个进程、多个服务实例或多个节点同时访问共享资源时的互斥问题。
一个可靠的分布式锁通常需要至少考虑以下几个目标:
- 互斥性:同一时刻尽量只有一个客户端进入临界区。
- 避免死锁:客户端崩溃或失联后,锁最终能够被释放。
- 锁归属校验:客户端不能误释放其他客户端持有的锁。
- 故障容忍:需要考虑网络分区、节点宕机、主从切换等异常情况。
- 业务兜底:关键业务不能仅依赖锁本身保证绝对正确,还应配合幂等、事务、CAS、唯一约束或 fencing token。
基于数据库的实现方案
基于数据库实现分布式锁,常见方式包括:
- 利用唯一索引或唯一约束竞争一条锁记录;
- 使用专门的锁表记录
owner、过期时间等信息; - 在事务中配合
SELECT ... FOR UPDATE实现悲观互斥; - 使用数据库提供的 Advisory Lock;
- 对只需要防止并发覆盖的数据更新场景,使用版本号或条件更新实现乐观并发控制。
多个请求
↓
竞争 Redis 中同一个 Key
↓
SET NX
┌─────────────┐
成功 失败
↓ ↓
获得锁 Key 已存在
需要注意的是,乐观锁属于并发控制机制,并不等价于传统意义上的分布式互斥锁。
例如:
UPDATE product
SET stock = stock - 1,
version = version + 1
WHERE id = 1
AND version = 10;
SQL这段 SQL 通过条件更新保证只有满足指定版本号的数据才能更新,本质上属于 CAS 风格的并发控制,而不是“客户端 A 获取锁后,客户端 B 必须等待”的互斥锁语义。
数据库方案的优势
数据库方案最大的优势是架构简单。
如果业务系统本身已经依赖 MySQL、PostgreSQL 等数据库,就不需要额外引入 Redis、ZooKeeper 或 etcd,可以直接利用现有数据库实现低频分布式协调。
同时,数据库可以借助事务、唯一约束和行锁等机制,在单主数据库和明确事务边界内提供较可靠的互斥控制。
例如,使用唯一索引竞争锁:
CREATE TABLE distributed_lock (
lock_name VARCHAR(128) PRIMARY KEY,
owner VARCHAR(128) NOT NULL,
expire_at DATETIME NOT NULL
);
SQL客户端尝试插入同一个 lock_name:
INSERT INTO distributed_lock(lock_name, owner, expire_at)
VALUES ('order_job', 'node-a', NOW() + INTERVAL 30 SECOND);
SQL如果同一锁名称已经存在,唯一约束会阻止第二个客户端再次插入。
数据库方案的局限
数据库锁通常涉及:
- SQL 执行;
- 索引维护;
- 事务;
- 持久化存储;
- 数据库连接;
- 行锁或表锁竞争。
因此在高锁竞争场景下,其延迟和吞吐通常不如 Redis 等内存型方案。
同时,如果事务持锁时间过长,还可能导致:
- 数据库连接长期占用;
- 锁等待增多;
- 事务堆积;
- 连接池耗尽;
- 整体数据库性能下降。
数据库方案还必须正确处理客户端异常退出问题。
如果只是简单插入一条锁记录,却没有设计过期时间或清理机制,那么客户端崩溃后可能留下永久锁。
另外,不能简单根据数据库具备 ACID 特性就推导出整个分布式数据库部署天然具有严格的分布式锁一致性。
如果数据库采用:
- 异步主从复制;
- 自动 Failover;
- 多主架构;
- 读写分离;
仍然需要额外分析复制延迟和故障切换对锁状态的影响。
适用场景
数据库锁更适合:
- 系统配置变更;
- 定时任务控制;
- 后台审批;
- 低频管理操作;
- 低竞争内部任务;
- 不希望增加额外基础设施的简单系统。
对于秒杀、实时交易等高并发低延迟场景,通常应考虑 Redis、ZooKeeper 或 etcd 等方案。
基于 Redis 的实现方案
Redis 是实际工程中最常见的分布式锁实现之一。
Redis 单实例分布式锁通常使用 SET 命令同时完成:
- 不存在时才写入;
- 设置锁持有者唯一标识;
- 设置自动过期时间。
例如:
SET order:123:lock 6b692918-2cab-4dc0-b360-481db67d4731 NX PX 30000
其中:
NX:只有 Key 不存在时才设置;PX 30000:设置 30 秒过期时间;- Value:使用 UUID 等随机唯一标识表示当前锁持有者。
不建议把:
SETNX lock value
EXPIRE lock 30
作为两个独立命令执行。
因为如果 SETNX 成功后客户端立即崩溃,而 EXPIRE 尚未执行,就可能留下一个永不过期的锁。
安全释放锁
释放 Redis 锁时不能直接执行:
DEL order:123:lock
因为当前客户端的锁可能已经过期,并且已经被另一个客户端重新获取。
例如:
Client A 获取锁
↓
Client A 长时间暂停
↓
A 的锁 TTL 到期
↓
Client B 获取同一把锁
↓
Client A 恢复并执行 DEL
↓
误删 Client B 的锁
因此释放锁时必须校验锁持有者身份。
可以使用 Lua 脚本保证“比较 Value”和“删除 Key”两个动作原子执行:
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
lua只有当锁中的唯一 Token 与当前客户端保存的 Token 一致时,才能删除。
Redis 的优势
Redis 的锁操作主要发生在内存中,因此通常具有:
- 较低延迟;
- 较高吞吐;
- 简单的 API;
- 成熟的客户端生态。
例如 Java 中的 Redisson 已经封装了:
- 自动续期;
- 可重入锁;
- 公平锁;
- 读写锁;
- 联锁;
- RedLock 等高级能力。
因此 Redis 非常适合高频、低延迟的业务协调。
Redis 的风险
Redis 分布式锁最大的争议点之一是主从故障切换。
典型 Redis 主从复制通常是异步的:
Client A
↓
Master 写入锁
↓
Master 尚未复制给 Replica
↓
Master 宕机
↓
Replica 晋升为新 Master
↓
Client B 获取同一把锁
此时 Client A 和 Client B 都可能认为自己成功持有锁。
所以:
Redis 单实例上的原子命令可以正确完成单节点锁操作,但如果依赖异步主从复制进行 Failover,就不能简单认为锁一定不会丢失。
Redis 官方提出过 Redlock 算法,通过多个相互独立的 Redis 实例减少单节点故障带来的影响。
不过 Redlock 的安全模型在分布式系统社区中存在争议。
实际工程中,更稳妥的原则是:
不要把 Redis 分布式锁当成业务正确性的唯一防线。
对于库存、订单、支付等关键数据,还应配合:
- 唯一约束;
- CAS;
- 数据库事务;
- 幂等;
- fencing token。
适用场景
Redis 锁通常适合:
- 秒杀;
- 防重复提交;
- 高频定时任务抢占;
- 缓存重建;
- 防止缓存击穿;
- 微服务内部短时间互斥;
- 对延迟比较敏感的业务。
基于 ZooKeeper 的方案
ZooKeeper 是经典的分布式协调系统,非常适合构建分布式锁、Leader Election、配置协调等机制。
ZooKeeper 分布式锁最经典的实现方式是:
EPHEMERAL + SEQUENTIAL
也就是临时顺序节点。
例如多个客户端尝试获得:
/locks/order
可以分别创建:
/locks/order/lock-00000001
/locks/order/lock-00000002
/locks/order/lock-00000003
客户端获取所有子节点后判断:
自己创建的节点是不是序号最小的节点。
如果是,就获得锁。
如果不是,则监听自己的前一个节点。
例如:
lock-00000001 ← 获得锁
lock-00000002 ← Watch 00000001
lock-00000003 ← Watch 00000002
当:
lock-00000001
被删除后,只需要通知:
lock-00000002
重新竞争。
这种设计可以避免所有客户端同时监听同一个节点而产生严重的惊群效应。
临时节点与 Session
ZooKeeper 的临时节点生命周期与 Session 绑定。
如果客户端正常退出或 Session 最终过期,ZooKeeper 会自动删除该客户端创建的临时节点。
因此:
Client A
↓
创建 EPHEMERAL_SEQUENTIAL
↓
获得锁
↓
Client A 崩溃 / Session 过期
↓
ZooKeeper 删除临时节点
↓
下一个客户端获得锁
这一机制能够很好地解决客户端异常崩溃后锁无法释放的问题。
需要注意:
短暂网络抖动并不会立即导致 Session 失效。
如果客户端能够在 sessionTimeout 范围内重新建立连接并恢复 Session,则临时节点仍然可以保留。
ZooKeeper 的一致性语义
ZooKeeper 不能简单描述成:
任意时刻所有节点看到的状态完全一致。
ZooKeeper 的写操作具有全局顺序和较强的一致性语义,但普通读操作可能从客户端当前连接的 Server 返回,因此可能读取到稍旧的数据。
ZooKeeper 锁协议能够可靠工作的关键在于:
- 全局有序更新;
- 临时顺序节点;
- Session;
- Watch;
- 正确的锁竞争协议。
而不是“任何节点上的任意读操作都严格线性一致”。
ZooKeeper 的优势
ZooKeeper 非常适合分布式协调:
- 临时节点自动释放;
- 顺序节点天然适合构建公平队列;
- Watch 可以避免频繁轮询;
- 锁、Leader Election 等 Recipe 非常成熟。
使用顺序节点时,可以按照节点序号组织竞争顺序,因此很适合实现 FIFO 风格的公平锁。
ZooKeeper 的缺点
ZooKeeper 的写操作需要经过一致性协议,因此相比 Redis 单节点内存操作:
- 延迟通常更高;
- 锁吞吐通常更低;
- 对网络和磁盘性能更加敏感。
具体性能取决于:
- 集群节点数;
- 磁盘性能;
- fsync 延迟;
- 网络 RTT;
- Watch 数量;
- 客户端数量;
- 读写比例;
- 锁竞争程度。
因此不应简单使用固定 QPS 描述 ZooKeeper 锁性能。
此外,ZooKeeper 集群还需要考虑:
- JVM;
- Snapshot;
- Transaction Log;
- 磁盘 I/O;
- Session;
- 节点监控;
运维成本通常高于 Redis。
适用场景
ZooKeeper 适合:
- Leader Election;
- 分布式任务调度;
- 强协调场景;
- 公平锁;
- 已经使用 ZooKeeper 的基础设施;
- 对锁自动释放和会话语义要求较高的系统。
基于 etcd 的方案
etcd 是 CNCF 毕业项目,也是 Kubernetes 控制平面最重要的基础组件之一。
etcd 底层使用 Raft 共识算法维护一致状态。
使用 etcd 构建分布式锁时,通常会涉及:
- Lease;
- Revision;
- Transaction;
- Compare-and-Swap;
- Watch;
- Lock API。
Lease
Lease 负责管理 Key 的生命周期。
例如客户端创建一个 TTL 为 10 秒的 Lease:
Lease TTL = 10s
然后把锁 Key 绑定到 Lease:
lock key
↓
Lease
客户端持续通过 KeepAlive 续约。
如果客户端崩溃、网络中断或无法继续续约:
KeepAlive 停止
↓
Lease TTL 到期
↓
锁 Key 被自动删除
这能够避免客户端异常退出后永久持锁。
但需要特别注意:
Lease 本身只负责生命周期管理,并不能单独提供互斥语义。
真正实现分布式锁,还需要配合:
- Transaction;
- CAS;
- Revision;
- 官方 Lock / concurrency 实现。
因此生产环境中通常不建议只用简单的:
Put + Lease
自行拼装完整锁协议。
如果客户端库提供官方的 concurrency / Lock API,应优先使用成熟实现。
Revision
etcd 中的 Revision 是集群范围内单调递增的逻辑序号。
它可以用于:
- 判断事件先后顺序;
- 构建锁竞争队列;
- Watch;
- 作为 fencing token 的重要基础。
例如:
Client A 获取锁 → Revision 101
Client B 获取锁 → Revision 102
下游资源可以拒绝小于当前最大 Revision 的旧请求。
这样即使 Client A 长时间暂停后恢复,由于其 Token 已经过期,也可以阻止旧客户端继续写入关键资源。
etcd 的一致性与性能
etcd 通过 Raft 对写入进行复制,并提供强一致的 KV 语义。
相比 Redis,etcd 的操作需要经过共识协议,因此单纯从延迟角度看通常更高。
但它在云原生环境中具备非常成熟的强一致协调能力。
etcd 官方性能测试表明,在合适硬件和网络环境下,三节点集群能够达到每秒数万次 API 请求。
需要注意:
API Benchmark 的 requests/s 不能直接等同于完整分布式锁的 Lock/Unlock TPS。
完整的锁操作可能涉及:
- Lease;
- Transaction;
- Revision;
- Watch;
- Unlock;
- 网络 RTT;
- 锁竞争。
因此实际锁吞吐必须结合具体场景测试。
etcd 的缺点
etcd 的生产部署通常至少需要 3 个节点,以保证多数派容错。
同时需要重点监控:
- Raft;
- Leader;
- 网络 RTT;
- 磁盘 fsync;
- DB Size;
- Compaction;
- Defragment;
- Lease;
- 节点健康状态。
因此运维复杂度通常高于单节点 Redis。
适用场景
etcd 特别适合:
- Kubernetes;
- 云原生控制平面;
- Leader Election;
- 分布式任务调度;
- 强一致配置管理;
- 需要 Lease + Watch + Revision 的协调系统。
基于 Consul 的方案
Consul 是 HashiCorp 提供的服务发现和分布式协调系统。
Consul 可以利用:
KV + Session
实现分布式锁。
客户端创建 Session:
Session
然后通过 KV acquire 操作尝试把某个 Key 与 Session 绑定:
lock/order
↓
Session A
如果 Key 当前没有被其他 Session 持有,则 acquire 可以成功。
客户端失联或 Session 失效后,锁可以根据 Session 配置进行释放。
Consul Session
Consul Session 可以与:
- Node;
- Health Check;
- TTL;
等状态关联。
因此可以把锁生命周期与服务实例状态结合起来。
这对于已经使用 Consul 服务发现体系的微服务非常方便。
Consul 多数据中心的正确理解
Consul 支持多数据中心 Federation,但不能简单理解为:
一个数据中心获取的 KV 锁,会通过 WAN Gossip 自动同步成所有数据中心共享的全局锁。
WAN Gossip 的主要作用是:
- Data Center 之间的 Server Membership 信息传播;
- 帮助 Server 发现其他数据中心;
- 支持跨数据中心请求路由。
它并不是用来复制 KV 锁状态的共识协议。
各数据中心内部的强一致状态主要由自己的 Raft 集群维护。
如果客户端要在远程数据中心操作锁,需要显式向目标 Data Center 发起请求。
因此:
Consul 原生支持多数据中心,并不等于天然提供一个通过 WAN Gossip 自动同步的全球分布式锁。
真正的跨地域全局互斥仍然需要根据业务架构单独设计。
Consul 的一致性语义
Consul 状态写入通过 Raft 完成。
但读取需要区分不同的一致性模式。
常见模式包括:
default;consistent;stale。
默认读取在绝大多数情况下能够提供较强一致性,但 Leader 切换期间存在很小的窗口可能读取到旧数据。
如果业务需要更严格的一致性读取,可以显式使用:
consistent
如果业务允许读取稍旧数据并希望降低延迟,则可以使用:
stale
因此不应简单在表格中把 Consul 的所有读写统一描述为“线性一致”。
Consul 的可重入语义
Consul 的 KV acquire 允许当前已经持有锁的同一 Session 再次 acquire。
但是,这种 Session 维度的再次 acquire 与 Java ReentrantLock 或 Redisson 那种:
Thread ID
+
Hold Count
形式的线程级可重入锁并不完全相同。
因此如果业务需要:
- 重入计数;
- 公平锁;
- 读写锁;
- 更复杂的锁语义;
通常需要在客户端或业务层进一步封装。
适用场景
Consul 锁更适合:
- 已经大量使用 Consul 的微服务架构;
- 服务发现与协调结合;
- Leader Election;
- 配置协调;
- 生命周期与服务健康状态强相关的任务。
选型决策与实践建议
分布式锁没有绝对统一的最佳方案。
Redis
适合:
- 高吞吐;
- 低延迟;
- 高频短时间互斥;
- 已经大量使用 Redis 的业务系统。
但业务必须能够正确应对:
- 锁 TTL 到期;
- 客户端暂停;
- 主从故障切换;
- 网络分区;
- 锁偶尔失效。
ZooKeeper
适合:
- 分布式协调;
- Leader Election;
- 公平排队;
- 任务调度;
- 已经依赖 ZooKeeper 的系统。
etcd
适合:
- Kubernetes;
- 云原生基础设施;
- 强一致协调;
- Leader Election;
- 需要 Lease、Revision、Watch 的系统。
Consul
适合:
- 已经使用 Consul 服务发现;
- Session 与服务实例生命周期结合;
- Consul 生态内部的协调任务。
数据库
适合:
- 低频锁;
- 低竞争;
- 后台管理;
- 简单内部系统;
- 不希望引入新的基础设施。
常见陷阱与最佳实践
1. 锁超时与业务执行时间不匹配
假设:
锁 TTL = 10 秒
业务执行 = 20 秒
可能出现:
Client A 获得锁
↓
执行 10 秒
↓
锁自动过期
↓
Client B 获得锁
↓
A 和 B 同时执行临界区
最佳实践
可以采用:
- 自动续期;
- 合理 TTL;
- 幂等;
- CAS;
- fencing token。
例如 Redisson Watchdog 会在业务仍持有锁时自动续期。
但必须明确:
自动续期只能降低锁过期概率,不能从理论上消除所有进程暂停、网络分区和故障切换问题。
关键业务仍应使用下游数据约束兜底。
2. 锁的可重入性
简单 Redis:
SET lock token NX PX 30000
默认并不具备 Java ReentrantLock 那样的可重入语义。
如果同一个线程再次获取同一把锁:
Thread A
↓
lock()
↓
调用 methodB()
↓
再次 lock()
就可能等待自己持有的锁。
最佳实践
如果业务需要可重入锁,可以使用:
- Redisson;
- 具备 reentrant mutex 的成熟客户端;
- 在业务代码层避免嵌套加锁。
不同中间件对“重入”的定义也可能不同,需要区分:
- Thread 级;
- Process 级;
- Session 级。
3. GC Pause、进程暂停与锁租约失效
Java、Go 等语言都具有垃圾回收机制。
因此不能说:
Go 是非 GC 语言。
Go 也拥有 Garbage Collector。
在极端情况下:
- Full GC;
- CPU 饥饿;
- 容器冻结;
- VM Pause;
- 操作系统调度延迟;
- 调试器暂停;
都可能让客户端长时间无法执行续约。
例如:
Client A 获取锁
↓
A Pause 60 秒
↓
Lease 30 秒后过期
↓
Client B 获取锁
↓
Client A 恢复
此时 A 可能继续操作共享资源。
错误解决方式
简单把 TTL 设置得非常大:
TTL = 10 分钟
只能降低概率,并不能从理论上证明:
暂停一定不会超过 10 分钟
Fencing Token
更加可靠的方式之一是使用 fencing token。
例如:
Client A → token 41
Client B → token 42
共享资源记录:
last_token = 42
当恢复后的 Client A 再次尝试写入:
token = 41
资源端发现:
41 < 42
直接拒绝操作。
因此:
分布式锁解决“谁当前被认为拥有执行权”,fencing token 解决“已经失去执行权的旧客户端还能不能继续写”。
对于支付、库存、金融、任务调度等关键场景,这一点非常重要。
4. 锁获取的公平性
不同方案的公平性并不相同。
ZooKeeper
可以利用:
EPHEMERAL_SEQUENTIAL
按照节点序号形成 FIFO 风格竞争队列。
因此非常适合实现公平锁。
etcd
etcd 可以利用:
Revision
表示事件先后顺序,官方 Lock 实现也可以据此组织竞争关系。
Redis
最简单的 Redis 锁通常采用:
失败
↓
Sleep
↓
Retry
这种方式默认不存在严格 FIFO 保证。
多个客户端同时重试还可能产生:
Thundering Herd
即惊群问题。
最佳实践
如果业务强依赖公平排队:
- ZooKeeper;
- etcd;
通常更容易构建。
如果只需要高性能互斥,而不要求严格公平:
- Redis;
通常更简单。
5. 释放锁时必须确认锁归属
Redis 中必须为锁保存唯一 Token:
SET order:lock 550e8400-e29b-41d4-a716-446655440000 NX PX 30000
释放时:
GET value
↓
是否等于自己的 token
↓
是 → DEL
否 → 不处理
而且:
GET + DEL
不能拆成两个普通命令,因为两条命令之间锁状态可能发生变化。
所以应该使用 Lua 或其他原子条件删除机制。
ZooKeeper 和 etcd 可以分别利用:
- Session + Ephemeral Node;
- Lease + Lock Key;
管理锁生命周期,因此在客户端异常退出后能够自动释放锁。
但必须注意:
Session / Lease 绑定解决的是生命周期问题,并不天然等于访问权限隔离。
是否允许其他客户端删除某个锁节点,还取决于:
- ZooKeeper ACL;
- etcd Authentication / RBAC;
- 客户端锁协议;
- 应用权限设计。
6. 不要把分布式锁当成绝对安全屏障
即使客户端成功获得锁,也必须考虑:
网络分区
GC Pause
VM Pause
Lease 过期
Master Failover
客户端超时
服务重启
因此关键系统的正确性通常应该由多层机制共同保证:
Distributed Lock
+
Idempotency
+
CAS
+
Database Transaction
+
Unique Constraint
+
Fencing Token
而不是:
Distributed Lock
=
绝对不会出现并发问题
方案对比
| 维度 | 数据库 | Redis | ZooKeeper | etcd | Consul |
|---|---|---|---|---|---|
| 锁性能 | 较低~中等 | 高 | 中等 | 中等~高 | 中等 |
| 核心机制 | 唯一约束 / 行锁 / 锁表 | SET NX PX + Token | 临时顺序节点 + Watch | Lease + Revision + Transaction / Lock | Session + KV Acquire |
| 一致性特点 | 取决于数据库拓扑和事务模型 | 单实例原子;Failover 需额外考虑锁安全 | 写操作有全局顺序;普通读可能稍旧 | 强一致 KV / Raft | Raft 写入;读取取决于 Consistency Mode |
| 自动释放 | 需要自行设计 | TTL / Watchdog | Session + Ephemeral Node | Lease | Session |
| 公平性 | 取决于实现 | 默认无严格保证 | 很适合实现 FIFO | 可利用 Revision 构建有序竞争 | 取决于实现 |
| 运维复杂度 | 低 | 低~中 | 高 | 中~高 | 中~高 |
| 生态优势 | 无需额外中间件 | 成熟、客户端丰富 | 分布式协调成熟 | Kubernetes / 云原生 | 服务发现与协调结合 |
| 典型场景 | 低频后台任务 | 高频短时间互斥 | Leader Election / 任务调度 | 云原生协调 | 已部署 Consul 的系统 |
不建议直接用固定 QPS 横向比较不同分布式锁方案。实际性能高度依赖硬件、网络、锁竞争程度、客户端数量、持锁时间、请求模型和实现方式。普通 KV Benchmark 也不能直接等同于完整 Lock/Unlock 的吞吐。
最终建议
选型时可以遵循以下思路:
如果系统已经大量使用 Redis
优先考虑 Redis。
适用于:
- 高并发;
- 低延迟;
- 短时间互斥;
- 可以通过幂等或数据约束兜底的业务。
如果需要成熟的分布式协调能力
考虑 ZooKeeper。
适用于:
- Leader Election;
- 公平锁;
- 分布式调度;
- 强协调系统。
如果系统属于 Kubernetes / 云原生体系
优先考虑 etcd。
它天然具备:
- Lease;
- Revision;
- Watch;
- Transaction;
- Lock;
- Raft;
非常适合构建控制面和分布式协调机制。
如果已经使用 Consul
可以直接使用 Consul:
Session + KV
实现锁。
但不要因为 Consul 支持多数据中心,就认为 WAN Gossip 会自动把锁复制成跨地域全局锁。
如果只是低频后台任务
数据库往往已经足够。
没有必要为了几个低频锁操作专门部署一个新的协调集群。
总结
Redis、数据库、ZooKeeper、etcd、Consul 都可以参与实现分布式协调,但它们解决问题的方式和安全边界并不相同。
可以简单理解为:
数据库
↓
架构简单,适合低频协调
Redis
↓
高性能,工程实践广泛
ZooKeeper
↓
成熟的分布式协调与公平竞争
etcd
↓
强一致 + 云原生 + Lease / Revision
Consul
↓
服务发现体系中的 Session 协调
真正重要的不是“哪个锁最快”,而是:
在锁失效、客户端暂停、网络分区和节点故障发生时,你的业务是否仍然能够保持正确。
对于非关键业务,可以使用成熟客户端提供的分布式锁能力解决大部分问题。
对于支付、库存、金融、调度等关键系统,则应该进一步配合:
- 幂等;
- CAS;
- 数据库事务;
- 唯一约束;
- fencing token;
共同保证系统最终的正确性。