Appearance
分布式同步与协调总览
一句话描述
单机里进程同步靠锁、信号量、内存屏障,内核顺手全给了;进程一旦分布到多台机器,共享内存没了、消息会丢会延迟会乱序、机器会"半死不活"(部分失败),于是需要一整套"分布式协调"原语来替代单机的同步手段。本文按三块梳理:互斥(分布式锁/租约)、选举(选主)、一致性屏障(同步汇合点),并把库内相关篇目串起来。
零、为什么单机的招式全部失效
没有共享内存:两个进程看不到同一个变量,锁状态必须放到"大家都够得着"的地方(一个服务、一张表、一个约定协议);
消息不可靠:请求会丢、响应会丢、超时不等于失败(对方可能已经执行了)——所有协调原语都要回答"对方到底做没做";
没有全局时钟:两台机器的时间戳不可比,"谁先谁后"只能靠逻辑序(版本号、term/epoch、事务 id);
部分失败:进程可能卡死而不退出、网络分区让"活着的多数"和"少数"各玩各的——由此引出 CAP 取舍与多数派(quorum),见 分布式理论-CAP原则。
一、互斥:分布式锁与租约
目标与单机锁相同:同一时刻只有一个客户端操作临界资源;差别是"锁管理器"本身也分布在网络上。
1、三种主流实现路线
| Redis 锁 | ZooKeeper 锁 | 数据库唯一约束 | |
|---|---|---|---|
| 原理 | SET key 唯一值 NX EX ttl | 临时顺序节点 + watch 前一个节点 | 唯一索引/insert 抢占锁表行 |
| 释放 | Lua:值是自己的才删 | 会话断开自动删除临时节点 | 删行/事务提交 |
| 死锁防护 | TTL 过期(忘续期会提前失守) | 会话超时自动释放 | 靠超时任务清理 |
| 一致性前提 | 主从切换可能丢锁(RedLock 争议) | ZAB 过半提交,较强 | 库可用即有效 |
| 复杂度/性能 | 低/高 | 中/中 | 低/低 |
Redis 锁的两个必做细节:value 写唯一标识(防误删别人的锁)+ 释放用 Lua 原子判断再删("读-比-删"必须一步完成);长任务要后台线程续期(看门狗,Redisson 已内置);
ZK 锁的"排号"设计:所有人到
/lock下建临时顺序节点,序号最小者持锁,其余只 watch 自己前一个节点——前驱释放才唤醒后继,避免惊群;会话断开临时节点自动消失,天然防死锁;**租约(Lease)**是锁的一般化:协调方授予"有限期的使用权",持有者定期心跳续约,到期自动作废——解决"持锁者宕机后锁永远不释放"。TTL 是权衡:太短心跳一抖就易主,太长故障切换慢。
2、绕不开的坑:锁过期后的"复活写"
客户端 A 拿锁 → GC 停顿/网络延迟超过 TTL → 租约易主给 B → A 苏醒后继续写——两个"持锁者"先后写同一资源;
解法是 fencing token(令牌递增,或说 epoch/term):资源方只接受"最新 token"的写,A 拿着旧 token 会被拒——注意这要求资源端配合校验,纯 Redis 锁给不了这个保证,这是 Martin Kleppmann 与 Redis 作者之争的核心。
二、选举:谁是主
为什么几乎所有协调系统都要选主:让只有一个节点推进状态(发号、提交决议),把并发问题内部化——Paxos/Raft/ZAB 殊途同归;
Raft 的选主骨架(详见 分布式理论-一致性协议Raft):节点状态 Follower/Candidate/Leader;选举超时随机化错开发起时机;Candidate 拉票,拿到多数派即当选;每个 term(任期)单调递增,旧 term 的消息一律拒绝——term 就是"逻辑时钟";
ZAB(ZooKeeper 的协议)类似:epoch + 事务 zxid 全序,_leader 恢复阶段_保证已提交的事务不丢(Paxos 族谱见 分布式理论-一致性算法Paxos);
脑裂与多数派:网络分区时少数派一侧绝不能同时出两个主——"票数过半"保证最多一边凑齐多数;旧主被分区隔离后恢复,发现 term 更大自动退位;应用侧同样要 fencing token 拒绝"过期领导者"的写;
库内例子:美团 Leaf-snowflake 用 ZooKeeper 顺序节点自动分配 workerID(弱依赖 ZK、本地文件兜底),见 美团-Leaf——美团点评分布式ID生成系统;服务发现组件对比(Eureka/etcd/Consul/ZK)见 「Chris Richardson 微服务系列」服务发现的可行方案以及实践案例。
三、一致性屏障:同步的汇合点
屏障(Barrier):约定"人齐了才放行"——全部 N 个参与者到达(或完成)前,谁都不能进入下一阶段。用于阶段间同步(MapReduce 的 map 全部结束才 reduce)、批量任务对齐、分布式测试起跑线;
双屏障(Double Barrier):入口"人齐才一起进",出口"人齐才一起出",适合"一组 worker 同进同出"的批次;
ZooKeeper 的教科书实现:屏障节点
/barrier,进入的子节点计数(或最小序号判断),不满足条件就 watch 屏障节点等通知,满足即放行——watch 机制把"轮询"变成"等通知";屏障与 2PC/3PC 的关系:两阶段提交本质是"预提交屏障 + 提交屏障"两道栅栏(协调者等全员 prepare,再统一下 commit),详见 分布式理论-2PC、分布式理论-3PC;BASE 的柔性取舍见 分布式理论-BASE。
四、协调服务一览:ZK / etcd / Consul
| ZooKeeper | etcd | Consul | |
|---|---|---|---|
| 共识协议 | ZAB | Raft | Raft |
| 数据模型 | znode 树 + watch | KV + watch(lease/TTL) | KV + 服务目录 + 健康检查 |
| 典型用途 | 锁/选主/配置/队列 | K8s 的元数据底座 | 服务发现 + 配置 |
| 使用姿势 | 客户端 CURator 等 | gRPC/HTTP API | HTTP + DNS |
共同抽象:强一致的小存储 + watch 通知 + 会话/租约超时——三者凑齐,锁、选举、屏障、配置、服务发现全都 overlays 在这套地基上;
工程提醒:协调服务只放"小而重要"的元数据(别当数据库用);能不引协调就不引——例如秒杀扣库存"只用原子性、不上分布式锁"的取舍,见 秒杀系统设计。
五、速答清单
"分布式锁的三个必备?"——互斥(任意时刻至多一个持有者)、防死锁(故障自动释放)、防误删(只删自己的锁);
"Redis 锁和 ZK 锁怎么选?"——性能/团队栈熟 Redis 就 Redis + 看门狗;正确性优先、已有 ZK 就 ZK;极高正确性要求要 fencing token;
"怎么防脑裂?"——多数派投票 + 单调递增 term/epoch + 旧主写被拒;
"屏障和锁的区别?"——锁是"互斥"(一次一个),屏障是"对齐"(人到齐一起走),解决的问题不同;
一句话总纲:互斥管"一次只能一个",选举管"大家认一个头",屏障管"人对齐了一起走"——三者在 ZK/etcd 这类强一致存储上都是几行节点的游戏。
2026-10 扩充(Round1 查漏)。