前面四篇分别钉了全景、客户端契约、Coordinator 与 Proxy。运维与排障时更常问的是另一类问题:加机器时该加哪种进程?读热点和写热点是不是同一条轴? 若把 FDB 当成「均匀加节点就均匀变快」的黑盒,会在 Sequencer 世代切换或单一热 key 上踩坑。
本文是系列第 5 篇:给出读写数据流总图,归纳角色扩展性与单点边界,并与 TiKV 的组件分离作对照。不展开 Resolver 算法、TLog 复制细节或 Redwood 页结构——那些是第 8、10、12 篇。
本文是「FoundationDB 内核」系列第 5 篇(共 18 篇)。→ 系列目录
篇目 核心内容 第 4 篇 · Proxy Grv / Commit 第 5 篇 · 角色拓扑 读写总图、扩展边界 第 6 篇 · Sequencer 版本与恢复
版本锚定:Zhou et al., SIGMOD 2021 §2.3.3 Read-Write Separation and Scaling;FoundationDB 7.x Architecture;对照 tikv-htap 第 1 篇 的四件套模型。
fdbcli status details可观察角色分布,本篇不伪造命令输出(实验台账待执行)。
一、读写总图
把第 1、4 篇的路径收成拓扑视图:实线是同步 RPC / 提交路径,虚线是异步日志搬运。
flowchart TB
client["Clients / Layers"]
subgraph control ["Control plane"]
coord["Coordinators"]
cc["Cluster Controller"]
dd["Data Distributor"]
rk["Ratekeeper"]
end
subgraph ts ["Transaction system"]
seq["Sequencer"]
grv["Grv Proxies"]
cp["Commit Proxies"]
res["Resolvers"]
end
subgraph ls ["Log system"]
tlog["Log Servers"]
end
subgraph ss ["Storage system"]
stor["Storage Servers"]
end
client --> grv
client --> cp
client -->|"versioned reads"| stor
grv --> seq
cp --> seq
cp --> res
cp --> tlog
tlog -.->|"pull / apply"| stor
coord --> cc
cc --> seq
cc --> dd
cc --> rk
rk -.-> grv
dd -.-> stor
1.1 读路径(客户端视角)
- 经 Grv Proxy 取得 read version(可被 Ratekeeper 节流)。
- 用缓存的 range → Storage Server 映射,把
get/ range 读打到对应 Storage。 - 映射过期则向 Commit Proxy 刷新(第 4 篇)。
- 只读事务可在客户端本地结束,不必走 Commit Proxy(论文 §2.4.1)。
读吞吐的主扩展轴是 Storage Server 数量与 shard 分布(论文 §2.3.3)。热 key 仍受副本数与单进程能力限制——官方 Known Limitations 也写明小范围热读不会自动无限加副本;那是应用/数据模型问题,第 16 篇再谈。
1.2 写路径(客户端视角)
- 写缓冲在客户端。
- Commit Proxy → Sequencer(commit version)→ Resolver(OCC)→ Log Server(durable)。
- Storage 异步追日志;提交成功不等于每一副本瞬时可读,但版本契约保证后续 read version 不会落后于已确认提交(第 6、9、11 篇)。
写扩展轴是 Commit Proxy、Resolver、Log Server 的规模与分片(论文 §2.3.3),外加 Storage 消化日志的能力(落后时 Ratekeeper / 排障介入)。
sequenceDiagram
participant C as Client
participant G as Grv Proxy
participant S as Storage
participant P as Commit Proxy
participant Q as Sequencer
participant R as Resolver
participant L as TLog
Note over C,S: Read path
C->>G: read version
G-->>C: V
C->>S: get @ V
S-->>C: value
Note over C,L: Write path
C->>P: commit
P->>Q: commit version
P->>R: OCC
P->>L: durable log
P-->>C: ok
L-->>S: async apply
二、扩展性矩阵:谁能加,谁是逻辑单例
| 角色 | 典型基数 | 扩展方式 | 失败时 |
|---|---|---|---|
| Coordinators | 奇数小集合 | 运维变更协调员集 | 失去配置多数则控制面危险 |
| Cluster Controller | 1(逻辑) | 选举替换 | 短暂失去编排 |
| Sequencer / Master | 1 / 世代 | 恢复换代 | 写管线暂停至新世代就绪 |
| Data Distributor | 1 | 重新招募 | 均衡暂停,读写下路径仍可能可用 |
| Ratekeeper | 1 | 重新招募 | 反压可能失效或保守 |
| Grv Proxy | 多 | 水平加进程 | 客户端换列表;世代内替换走恢复策略 |
| Commit Proxy | 多 | 水平加进程 | 同上 |
| Resolver | 多(按 key 空间划分) | 加进程并调整范围 | 同上 |
| Log Server | 多 | 加进程;复制度配置 | 影响提交耐久与恢复 |
| Storage Server | 最多 | 加进程;Data Distributor 搬 shard | 读路径局部降级 |
论文强调:控制面上的单例与 Coordinators 不是性能主瓶颈,因为它们做的是有限元数据操作(§2.3.3)。性能主矛盾在 Storage 读、以及 TS/LS 写管线。逻辑单例的真正风险是 可用性 / 恢复 MTTR,不是「单线程算版本算不过来」的日常叙事——当然极端连接风暴仍可能压垮 Sequencer,故有 Proxy 批处理与 Ratekeeper。
2.1 「进程」与「角色」
官方
Architecture:集群尝试把不同角色招到不同进程,但无状态角色可能共址同一进程以满足招募目标。看
fdbcli status
时,不要假设「一进程一角色」或「机器数 = Storage
数」。容量规划应按角色 CPU / 磁盘画像拆(第 16
篇),而不是只数物理机。
三、与 TiKV 组件分离的对照
| 维度 | FoundationDB | TiKV 族(见 tikv-htap) |
|---|---|---|
| 元数据 / 调度 | Coordinators + Cluster Controller + Data Distributor | PD(路由、调度、TSO) |
| 全局版本 | Sequencer(经 Proxy) | PD TSO |
| 冲突 / 事务 | Resolver OCC + 客户端缓冲写 | Percolator 锁 + 2PC(TiDB 协调) |
| 提交耐久 | Log Server 集合 | Region Raft 日志 |
| 用户读 | Storage Server 直接服务 | Region Leader(默认)上读 |
| SQL / 记录模型 | 进程外 Layer | TiDB 无状态 SQL 层 |
两者都「拆组件」,但切开的刀法不同:
- TiKV:按 Key 空间切成大量独立 Raft 组,事务跨组用 Percolator。
- FDB:按职责切成管线角色,数据切 shard 主要服务存储与日志队列,提交正确性靠全局版本 + OCC。
flowchart LR
subgraph fdb ["FDB cut"]
f1["Roles: version / OCC / log / storage"]
end
subgraph tikv ["TiKV cut"]
t1["Many Raft groups per Region"]
end
fdb -->|"global OCC window"| contrast["Strict serializability path"]
tikv -->|"per-Region Raft + SI"| contrast2["Snapshot isolation path"]
选型含义留给第 18 篇;此处只要排障时不要拿「看 Region Leader」的手法硬套 FDB,或反过来用「找 Sequencer」去理解 TiKV 写延迟。
常见误解
- 「Storage 最多所以加 Storage 一定降低提交延迟」。提交延迟由 Proxy → Sequencer → Resolver → TLog durable 决定;Storage 主要影响读与 apply 滞后。
- 「多 Proxy 就能提高严格可串行化的冲突成功率」。冲突由数据争用与 OCC 窗口决定;多 Proxy 只分摊编排负载。
- 「和 TiKV 一样,副本数加到 5 就能线性提升单 key 读」。两边都有热 key 物理上限;FDB 文档对热读限制有明确说明(Known Limitations)。
开放问题
Unbundled 把运维对象从「N 个同构副本」变成「多角色异构进程」:监控指标、版本升级、故障域划分都更细。长期成本是否低于 Multi-Raft,没有单一答案——取决于团队是否吃得消角色级排障(第 17 篇)以及负载是否符合低冲突假设(论文 §2.3.2)。这是机制选择,不是营销对比。
四、把总图当成排障索引
后续第 17 篇会给症状决策树;这里先把「慢 / 失败」粗分到拓扑位置,避免第一反应永远是「加机器」。
| 现象(概念) | 先看的角色 | 为什么 |
|---|---|---|
| 新事务迟迟拿不到 read version | Grv Proxy、Ratekeeper、Sequencer | 发版本被节流或世代异常 |
提交长期 not_committed |
数据模型冲突、Resolver | OCC 争用,不是磁盘慢 |
| 提交成功但读偶发旧值感 | Storage 落后 TLog | 异步 apply;对比副本 |
大面积 transaction_too_old |
客户端事务过长、或刚恢复快进 | 窗口与世代,不是单 key |
| 写暂停、读仍可能部分可用 | 事务系统恢复中 | Sequencer/写子系统换代 |
flowchart TD
symptom["Symptom: latency or errors"]
symptom --> q1{"Get read version slow?"}
q1 -->|yes| grv["Inspect Grv / Ratekeeper / Sequencer"]
q1 -->|no| q2{"Commits abort?"}
q2 -->|yes| occ["Inspect conflicts and key design"]
q2 -->|no| q3{"Commit ok, read odd?"}
q3 -->|yes| lag["Inspect Storage lag vs TLog"]
q3 -->|no| topo["Inspect recovery / coordinators"]
4.1 容量叙事的正确打开方式
论文称 Storage 占进程多数、形成分布式 B-tree(§2.3.2)。因此:
- 容量(字节) 主要随 Storage 与复制因子增长。
- 写提交能力 随 Commit Proxy / Resolver / Log 与日志盘能力增长。
- 读能力 随 Storage 与 shard 均衡增长,但受热 key 限制。
把三种能力当成一个「集群 QPS」指标,是拓扑上的范畴错误。第 16 篇会按角色写资源画像,本篇只要求读者在看监控时分栏。
4.2 Data Distributor 与 Ratekeeper 在图上的位置
二者不在客户端读写热路径上,却决定「图能不能长期保持健康」:
- Data Distributor:监视 Storage,决定 range 归属与搬迁,避免磁盘与负载倾斜(官方 Architecture;论文 §2.3.1)。
- Ratekeeper:在系统接近饱和时压低 read version 发放,保护 TLog/Storage 不被提交洪水拖垮。
缺少 Distributor 时,短期读写仍可能成功,但热点与不均衡会累积;缺少有效 Ratekeeper 时,过载更常以级联超时与恢复抖动出现。拓扑图上把它们画在控制面,是为了提醒:扩展 Storage 却不管分布与反压,等于只加了磁盘。
flowchart TB
dd["Data Distributor"] -->|"move shards"| ss["Storage Servers"]
rk["Ratekeeper"] -->|"throttle"| grv["Grv Proxies"]
ss -->|"load signals"| dd
ss -->|"load signals"| rk
grv -->|"admission"| clients["New transactions"]
五、小结
三句话小结
- 读:Grv Proxy 取版本 → Storage 直接读;写:Commit Proxy 编排 Sequencer / Resolver / TLog,Storage 异步 apply。
- 可水平扩展的是 Proxy、Resolver、Log、Storage;Sequencer 与 Cluster Controller 等是逻辑单例,靠快速恢复保可用。
- 与 TiKV「多 Raft 组」相比,FDB 用角色管线切问题;扩展轴与排障入口都不同。
下一篇专攻版本权威:Sequencer:全局版本与恢复。
参考资料
核心论文
- Zhou et al., SIGMOD 2021, §2.3.2–2.3.3、§2.4.1(角色、读写分离扩展、事务路径)。
规范 / 文档
- FoundationDB Architecture(7.x)。
- FoundationDB Known Limitations — Limited read load balancing(7.x)。
站内
上一篇:Proxy:读版本与提交入口
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【FoundationDB 内核】Unbundled 全景:事务、日志与存储为何拆开
以 SIGMOD 2021 为锚点,厘清 FoundationDB 控制面与数据面角色分工,说明一次事务穿过 Proxy、Sequencer、Resolver、TLog、Storage 的路径,并与站内 distributed/39、tikv-htap 划清边界。
【FoundationDB 内核】Sequencer:read/commit version、逻辑单点与恢复
说明 Sequencer 如何分配 read version 与 commit version、为何是逻辑单点而非无高可用,以及世代恢复如何与版本推进衔接。
【FoundationDB 内核】写事务流水线:Client → Commit Proxy → Sequencer → Resolver → TLog
钉住 FoundationDB 一次写事务的提交路径:Client 经 Commit Proxy 取 commit version、Resolver 做 OCC、TLog 多数持久化后才向客户端返回成功;并划清「提交成功」与「Storage Server 可读」之间的边界。
【FoundationDB 内核】Storage Server:Shard、版本读取与读路径
说明 FoundationDB Storage Server 如何托管 shard、按版本服务读、从 TLog 异步拉日志并参与迁移;强调读路径直达 Storage、不经过 Commit Proxy 提交流水线,以及与 Data Distributor 的边界。