Skip to content

分布式同步与协调总览 ​

一句话描述 ​

单机里进程同步靠锁、信号量、内存屏障,内核顺手全给了;进程一旦分布到多台机器,共享内存没了、消息会丢会延迟会乱序、机器会"半死不活"(部分失败),于是需要一整套"分布式协调"原语来替代单机的同步手段。本文按三块梳理:互斥(分布式锁/租约)、选举(选主)、一致性屏障(同步汇合点),并把库内相关篇目串起来。

零、为什么单机的招式全部失效 ​

  • 没有共享内存:两个进程看不到同一个变量,锁状态必须放到"大家都够得着"的地方(一个服务、一张表、一个约定协议);

  • 消息不可靠:请求会丢、响应会丢、超时不等于失败(对方可能已经执行了)——所有协调原语都要回答"对方到底做没做";

  • 没有全局时钟:两台机器的时间戳不可比,"谁先谁后"只能靠逻辑序(版本号、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 ​

ZooKeeperetcdConsul
共识协议ZABRaftRaft
数据模型znode 树 + watchKV + watch(lease/TTL)KV + 服务目录 + 健康检查
典型用途锁/选主/配置/队列K8s 的元数据底座服务发现 + 配置
使用姿势客户端 CURator 等gRPC/HTTP APIHTTP + DNS
  • 共同抽象:强一致的小存储 + watch 通知 + 会话/租约超时——三者凑齐,锁、选举、屏障、配置、服务发现全都 overlays 在这套地基上;

  • 工程提醒:协调服务只放"小而重要"的元数据(别当数据库用);能不引协调就不引——例如秒杀扣库存"只用原子性、不上分布式锁"的取舍,见 秒杀系统设计。

五、速答清单 ​

  • "分布式锁的三个必备?"——互斥(任意时刻至多一个持有者)、防死锁(故障自动释放)、防误删(只删自己的锁);

  • "Redis 锁和 ZK 锁怎么选?"——性能/团队栈熟 Redis 就 Redis + 看门狗;正确性优先、已有 ZK 就 ZK;极高正确性要求要 fencing token;

  • "怎么防脑裂?"——多数派投票 + 单调递增 term/epoch + 旧主写被拒;

  • "屏障和锁的区别?"——锁是"互斥"(一次一个),屏障是"对齐"(人到齐一起走),解决的问题不同;

  • 一句话总纲:互斥管"一次只能一个",选举管"大家认一个头",屏障管"人对齐了一起走"——三者在 ZK/etcd 这类强一致存储上都是几行节点的游戏。

2026-10 扩充(Round1 查漏)。