FoundationDB 事务提交管线(Commit Pipeline)深度解析:从版本分配到冲突检测的完整写路径
【免费下载链接】foundationdbFoundationDB - the open source, distributed, transactional key-value store项目地址: https://gitcode.com/gh_mirrors/fo/foundationdb
导读
本文聚焦 FoundationDB 写路径的核心编排组件——事务提交管线(Transaction Commit Pipeline)。它由 Commit Proxy(提交代理)、GRV Proxy(读版本代理)、Master/Sequencer(主节点/序列器)与 Resolver(解析器)四个角色协作构成,完成从客户端提交请求到返回 commit version 的全过程:版本分配、冲突检测、提交批处理与持久化日志写入。读完本文,你将掌握这四个角色的职责边界、五阶段提交批处理的内部实现、基于令牌桶的集群级流控机制,以及版本号与墙钟对齐、SkipList 冲突检测等底层原理,并能在 fdbserver/commitproxy/、fdbserver/grvproxy/、fdbserver/resolver/、fdbserver/sequencer/ 等目录中直接对照源码深入研读。
本文基于仓库内设计文档 subsystem_05_commit_pipeline.md 展开,并结合对应源码(约 12K 行规模)进行验证与扩充。配套流程示意图见 diagram_05_commit_pipeline.md。
一、管线总览:四个角色如何协作提交一笔事务
FoundationDB 的提交管线将写路径拆解为四个严格串行、彼此通过消息异步衔接的角色:
Client ──CommitTransactionRequest──▶ CommitProxy │ Phase 1: GET VERSION ▼ Master/Sequencer (monotonic version) Phase 2: RESOLVE ▼ Resolver(s) (conflict detection) Phase 3: LOG ▼ TLog replicas (durable write) Phase 4: REPLY ▼ Client (committed!)- Commit Proxy:提交管线的"主力"(workhorse),负责把客户端的提交请求攒成批次,并驱动整个批次走完所有阶段;
- Master/Sequencer:单调递增 commit version 的唯一来源;
- Resolver(s):纯冲突检测,维护已提交写范围的滑动窗口;
- TLog replicas:持久化落盘,达成仲裁确认后即视为 durable。
从源码看,这个流程的主循环位于 fdbserver/commitproxy/CommitProxyServer.cpp 的commitBatchactor(约 L2170-L2187),它依次co_await五个阶段:preresolutionProcessing→getResolution→postResolution→transactionLogging→ 发送 reply:
co_await CommitBatch::preresolutionProcessing(pContext); co_await CommitBatch::getResolution(pContext); co_await CommitBatch::postResolution(pContext); co_await CommitBatch::transactionLogging(pContext);对应地,源码中用一组字符串常量标记每个阶段(CommitProxyServer.cpp:491-498):preResolution、resolution、postResolution、transactionLogging、reply、complete,用于 Trace 事件与阶段耗时统计。
二、Commit Proxy:五阶段提交批处理
2.1 ProxyCommitData:代理的全局状态
Commit Proxy 的核心状态保存在ProxyCommitData(见 fdbserver/commitproxy/ProxyCommitData.h:272):
struct ProxyCommitData { MasterInterface master; // for version assignment std::vector<ResolverInterface> resolvers; // for conflict detection Reference<LogSystem> logSystem; // for TLog push IKeyValueStore* txnStateStore; // persistent metadata NotifiedVersion committedVersion; // largest durable version NotifiedVersion version; // current proxy version KeyRangeMap<ServerCacheInfo> keyInfo; // key range → storage servers + tags const std::vector<Tag>& tagsForKey(StringRef key); // tag lookup for mutations };关键字段的作用:
master与resolvers分别指向版本分配与冲突检测的服务端;logSystem是代理向 TLog 推送的抽象(支持多副本与版本向量等模式);txnStateStore是代理自带的持久化 KV,保存恢复事务、元数据等系统状态;committedVersion标记"已持久化"的版本水位,version是代理当前推进到的版本;keyInfo是KeyRangeMap<ServerCacheInfo>,把 key 区间映射到负责的存储服务器及其 Tag——这是后续tagsForKey()的数据来源。
2.2 CommitBatchContext:一个批次的全部上下文
每一批被处理的提交都封装在CommitBatchContext中(CommitProxyServer.cpp:500):
struct CommitBatchContext { std::vector<CommitTransactionRequest> trs; // batch of transactions LogPushData toCommit; // serialized mutations for TLogs Version commitVersion, prevVersion; std::vector<ResolveTransactionBatchReply> resolution; // conflict results std::set<Tag> writtenTags; };除此之外,该结构还携带了大量批量处理所需的中间量:transactionResolverMap(每笔事务由哪个 resolver 处理)、txReadConflictRangeIndexMap(冲突 key 报告用)、committed(每笔事务的冲突结果位图)、storeCommits(元数据提交队列)、idempotencyKVBuilder(幂等键处理)、hotShards检查(热分片限流)等。writtenTags记录本批次实际写入的 Tag 集合,其中writtenTagsPreResolution是解析前(不含 resolver 反馈修正)的预计算集合,用于版本向量(Version Vector)场景下的 TLog 单播优化(见ENABLE_VERSION_VECTOR_TLOG_UNICAST开关,CommitProxyServer.cpp:890)。
2.3 五个阶段逐一拆解
Phase 1:Pre-Resolution(预解析)—preresolutionProcessing(CommitProxyServer.cpp:827)
本阶段核心任务:
- 批次顺序控制与队列保护:通过
latestLocalCommitBatchResolving.whenAtLeast(localBatchNumber - 1)保证批次严格有序;若排队延迟超过MAX_READ_TRANSACTION_LIFE_VERSIONS / VERSIONS_PER_SECOND(即一版事务的生命周期),且PROXY_REJECT_BATCH_QUEUED_TOO_LONG开启,整个批次会直接以transaction_too_old拒绝(恢复事务除外,否则恢复永远无法完成,源码注释明确指出这一点)。 - 向 Master 申请 commit version:构造
GetCommitVersionRequest发给master.getCommitVersion,拿到versionReply.version(本批次提交版本)与versionReply.prevVersion(上一版本),并据此更新keyResolvers的区间映射(resolver 变更信息)。 - 校验事务大小、计算读/写冲突区间:由后续
getResolution阶段的ResolutionRequestBuilder完成冲突区间的统计与打包;同时统计maxTransactionBytes(单笔最大事务字节数)用于后续限流判断。 - 确定每个 mutation 的存储服务器 Tag:通过
tagsForKey(mutation.key)查询keyInfo,为每条 mutation 映射到目标存储服务器对应的 Tag。 - 检查热分片限流:若
HOT_SHARD_THROTTLING_ENABLED开启且hotShards非空,调用checkHotShards()清理过期分片并对热点分片上的写进行节流(CommitProxyServer.cpp:894)。
Phase 2:Resolution(解析/冲突检测)—getResolution(CommitProxyServer.cpp:936)
- 用
ResolutionRequestBuilder为每笔事务构造ResolveTransactionBatchRequest; - 并行向所有resolver 发送请求(每个 resolver 负责 key 空间的一个分区);当只有一个 resolver 时走单请求快速路径,否则用
getAllAsync等待全部应答(CommitProxyServer.cpp:967-1016); - 期间通过
releaseResolvingAfter控制并发批次数量,并对长时间无响应的 resolver 做连接重置(RESET_RESOLVER_BATCHES/RESET_RESOLVER_DELAY兜底); - 汇总后的
resolution向量中,每笔事务的冲突判定结果随后被应用到committed位图。
Phase 3:Post-Resolution(后处理)—postResolution(CommitProxyServer.cpp:1583)
- 解析冲突结果,标记冲突事务(冲突者将收到
not_committed); - 应用元数据变更:
applyMetadataToCommittedTransactions()处理数据库配置、存储服务器映射等系统键的变更,并写入txnStateStore(storeCommits队列); - 更新存储服务器映射(含 resolver 返回的变更);
- 处理幂等键(
IdempotencyIdKVBuilder),支持客户端幂等重试; - 依据 resolver 返回的每个 Tag 的提交版本信息(
tpcvMap)修正writtenTags。
Phase 4:Transaction Logging(日志写入)—transactionLogging(CommitProxyServer.cpp:1890)
- 通过
logSystem->push()把带 Tag 的 mutation 序列化推送(LogPushData); - 阻塞等待 TLog 仲裁确认(durable ack)后才继续;
- 更新
queueCommittedVersion(提交版本队列),并最终推进committedVersion水位; - 该阶段还负责把元数据提交(
storeCommits)写入磁盘 KV,保证系统状态与数据版本一致。
Phase 5:Reply(应答)
- 向每个客户端发送
CommitTransactionReply; - 冲突事务返回
not_committed错误;成功事务携带其 commit version 返回,供客户端进行后续读(read-your-writes)。
三、GRV Proxy:读版本分配与集群级流控
GRV Proxy(fdbserver/grvproxy/GrvProxyServer.cpp)为客户端事务分配读版本(read version)。它更是全集群流量控制(flow control)的主要执行点:ratekeeper 的背压决策在这里转化为对客户端请求的具体延迟与拒绝。
其核心状态GrvProxyData(约 L202):
struct GrvProxyData { MasterInterface master; // version source VersionVector ssVersionVectorCache; // storage server version tracking Version version; };3.1 端到端流控闭环:Ratekeeper → GRV Proxy → Client
流控是防止存储服务器或 TLog 落后时集群被压垮的反馈回路,分四步:
Step 1:Ratekeeper 计算全局 TPS 限额(Ratekeeper::updateRate(),fdbserver/ratekeeper/Ratekeeper.cpp:633)
- 周期性运行,周期为
METRIC_UPDATE_RATE(默认 0.1 秒,见 fdbserver/core/ServerKnobs.cpp:1053,慢速模拟时可调为 0.5); - 轮询每个存储服务器与 TLog 的队列深度、durable 字节速率与可用磁盘空间;
- 分别独立计算
normalLimits(DEFAULT + SYSTEM 优先级)与batchLimits(BATCH 优先级)的tpsLimit; - 核心公式(Ratekeeper.cpp:714-754):先计算
targetRateRatio——由存储/TLog 队列相对目标值(target)与 spring 阈值的饱满程度决定,targetRateRatio = min((storageQueue - targetBytes + springBytes) / springBytes, 2.0);随后把平滑后的实际 TPS(smoothedRate)按inputRate * targetRateRatio的比例缩放得到x,再据此反推tpsLimit。直观效果是:队列处于目标值以内时tpsLimit ≈ ∞;队列越过目标值后,限额随队列增长逐渐趋近于 0; - 批处理限额使用更激进的阈值(更大的
storageTargetBytes、logTargetBytes),因此批处理事务被优先限流; - 最终
tpsLimit还会被夹在RATEKEEPER_MIN_RATE(0.0)与RATEKEEPER_MAX_RATE(1e9)之间(Ratekeeper.cpp:1104-1108),并在特殊场景(如写延迟超限、磁盘满)直接置 0 或退化为RATEKEEPER_DEFAULT_LIMIT。
Step 2:Ratekeeper 向每个代理下发限额(handleGetRateInfoReqs(),Ratekeeper.cpp:351)
- 每个 GRV Proxy 周期性地(约
leaseDuration/2秒,带抖动)向 ratekeeper 发送GetRateInfoRequest; - ratekeeper 回复
transactionRate = normalLimits.tpsLimit / numProxies与batchTransactionRate = batchLimits.tpsLimit / numProxies——把全局限额平均分摊到所有 GRV Proxy; - 回复同时携带
leaseDuration(默认等于METRIC_UPDATE_RATE= 0.1 秒):若代理在租约内没有收到新限额,就调用GrvTransactionRateInfo::disable()禁用限速——把允许速率平滑降到 0 并停止释放事务,避免"失联后仍按旧限额放行"造成压垮。
Step 3:GRV Proxy 用令牌桶执行限速(GrvTransactionRateInfo,fdbserver/grvproxy/GrvTransactionRateInfo.h)
- 每个代理维护两个
GrvTransactionRateInfo:normalRateInfo(SYSTEM + DEFAULT)与batchRateInfo(BATCH); - 每个对象是一个平滑令牌桶:
setRate()把 ratekeeper 给的速率经Smoother平滑后写入;startReleaseWindow()用"允许速率与实际释放速率的平滑差值 × 时间窗口"计算本窗口limit; canStart(numAlreadyStarted, count)判定numAlreadyStarted + count <= limit + budget;budget累积跨窗口未使用的容量,但当队列为空时受maxEmptyQueueBudget上界约束,防止陈旧预算造成突发(endReleaseWindow()在每个释放窗口结束时结算 budget,见 GrvTransactionRateInfo.h:64);- 平滑机制(
Smoother)避免了速率突变导致的震荡。
Step 4:transactionStarter循环按优先级释放批次(transactionStarter(),GrvProxyServer.cpp:939)
- 每个 GRVTimer 周期内,按严格顺序排空三个优先级队列:SYSTEM → DEFAULT → BATCH;
- SYSTEM 事务完全绕过限速(从不检查
canStart)——保证系统关键事务不被饿死; - DEFAULT 事务由
normalRateInfo.canStart()把关; - BATCH 事务由
batchRateInfo.canStart()把关,使用更激进的批处理限额; - 通过限速的事务被合并到一次
getLiveCommittedVersion()调用发给 master,批次内所有客户端的应答同时返回——这正是批量吞吐的关键:把 N 次 master 往返摊薄为 1 次。
3.2 基于回复延迟的动态批处理
GRV 的批处理间隔会根据实测往返延迟自适应调节(queueGetReadVersionRequests,GrvProxyServer.cpp:539):
- 每批非 risky-read 请求完成后,
timeReply()测量往返时间并喂给normalGRVLatency流; queueGetReadVersionRequests据此计算:
target_latency = reply_latency * START_TRANSACTION_BATCH_INTERVAL_LATENCY_FRACTION GRVBatchTime = α * target_latency + (1 - α) * GRVBatchTime并夹在[START_TRANSACTION_BATCH_INTERVAL_MIN, START_TRANSACTION_BATCH_INTERVAL_MAX]之间(GrvProxyServer.cpp:650-655);
- 当首个请求进入空队列时,调度 GRVTimer,延迟为
max(0, GRVBatchTime - timeSinceLastGRV); - 效果:系统快时批间隔缩小(客户端延迟更低);系统慢时批次变大(更好地摊薄 master 往返)。
对应 Knob 默认值(fdbserver/core/ServerKnobs.cpp:832-835):
| Knob | 默认值 | 含义 |
|---|---|---|
START_TRANSACTION_BATCH_INTERVAL_MIN | 1e-6 s | 批间隔下界 |
START_TRANSACTION_BATCH_INTERVAL_MAX | 0.010 s | 批间隔上界 |
START_TRANSACTION_BATCH_INTERVAL_LATENCY_FRACTION | 0.5 | 目标延迟 = 回复延迟 × 0.5 |
START_TRANSACTION_BATCH_INTERVAL_SMOOTHER_ALPHA | 0.1 | 平滑系数 α |
3.3 压力下的低优先级丢弃
当 GRV 总队列深度超过START_TRANSACTION_MAX_QUEUE_SIZE(默认 1e6,见 fdbserver/core/ServerKnobs.cpp:843)时,代理通过"逐级淘汰"保护系统(queueGetReadVersionRequests,GrvProxyServer.cpp:541):
- 新到的BATCH请求立即以
grv_proxy_memory_limit_exceeded拒绝; - 新到的DEFAULT请求先从 BATCH 队列头部驱逐一笔(若有);若 BATCH 队列为空,则 DEFAULT 请求自身被拒绝;
- 新到的SYSTEM请求先驱逐一笔 BATCH,其次驱逐一笔 DEFAULT;仅当两个队列都为空时 SYSTEM 请求才会被拒绝;
- 另外,当 ratekeeper 下发的批处理速率趋近于 0(
batchRateInfo.getRate() <= 1/numProxies)时,BATCH 请求在入队前就立即以batch_transaction_throttled被拒(GrvProxyServer.cpp:605)。
3.4 GetReadVersion 完整流程
- 入队(
queueGetReadVersionRequests,L539):三个优先级队列 + 上述动态批处理; - 取版本(
getLiveCommittedVersion,GrvProxyServer.cpp:696):- 向 master 发
getLiveCommittedVersion请求; - 因果读校验(除非
CAUSAL_READ_RISKY):调用updateLastCommit(),通过logSystem->confirmEpochLive()确认当前 epoch 仍然存活——确保返回的版本确实代表已提交状态; - 若启用版本向量:把 master 的 delta 应用到本地 SS 版本缓存;
- 向 master 发
- 应答(
sendGrvReplies,GrvProxyServer.cpp:782):- 把版本发给批次内所有请求;
- 附带每个 Tag 的 throttle 信息,客户端可据此自限流;
- 若检测到持续限流(持续超过
GRV_SUSTAINED_THROTTLING_THRESHOLD秒,该 Knob 位于 CLIENT_KNOBS,见 GrvProxyServer.cpp:844),设置rkBatchThrottled/rkDefaultThrottled标志,通知客户端退避。
此外,GRV Proxy 还配套了专项测试与辅助模块:GrvProxyStarvationTests.cpp 验证优先级队列不被饿死,GrvQueueDelay.cpp 与 GrvQueueDelayTests.cpp 负责队列延迟建模与测试,HealthMetricsRequestServer.cpp 提供健康指标查询。
四、Master/Sequencer:单调版本的唯一来源
4.1 MasterData
Master 的状态封装在MasterData(fdbserver/sequencer/MasterData.h:50):
struct MasterData { Version lastEpochEnd; // last version from prior epoch Version recoveryTransactionVersion; // first version in this epoch NotifiedVersionValue liveCommittedVersion; // largest live-committed version Version version; // last assigned version double lastVersionTime; // timestamp of last version Optional<Version> referenceVersion; // for wall-clock alignment std::map<UID, CommitProxyVersionReplies> lastCommitProxyVersionReplies; VersionVector ssVersionVector; // per-SS commit versions ResolutionBalancer resolutionBalancer; };lastEpochEnd与recoveryTransactionVersion划定了每个 epoch(恢复周期)的版本区间,保证跨 epoch 版本单调;lastCommitProxyVersionReplies按代理缓存最近应答,用于请求去重与乱序处理;ResolutionBalancer(fdbserver/sequencer/ResolutionBalancer.cpp)负责在多个 resolver 之间均衡冲突检测的负载分配。
4.2 版本分配:getVersion()(masterserver.cpp:74)
- 代理校验:在
lastCommitProxyVersionReplies中查找请求方;未注册的代理(如重复招募产生的陈旧请求)直接send(Never())挂起; - 请求排序:
latestRequestNum.whenAtLeast(requestNum - 1)等待前一个请求被处理,保证同一代理的版本请求严格有序; - 去重:若
requestNum已处理过,直接返回缓存回复;若请求号偏旧,则挂起该请求; - 版本计算(masterserver.cpp:117-133):
toAdd = max(1, min(MAX_READ_TRANSACTION_LIFE_VERSIONS, VERSIONS_PER_SECOND * (now - lastVersionTime)))VERSIONS_PER_SECOND默认 1e6(每秒 100 万版本,fdbserver/core/ServerKnobs.cpp:151);MAX_READ_TRANSACTION_LIFE_VERSIONS默认5 * VERSIONS_PER_SECOND(ServerKnobs.cpp:152),即一次最多推进 5 秒的版本量,防止长时间空闲后一次性跨度过大;模拟环境按事务超时秒数换算;- 若设置了
referenceVersion,则调用figureVersion()向墙钟对齐(见下);否则简单累加version += toAdd;
- 应答:返回
GetCommitVersionReply,携带prevVersion与version。
注意源码中的两个边界探针(CODE_PROBE):version - prevVersion == 1(最小版本间隔)与version - prevVersion == MAX_READ_TRANSACTION_LIFE_VERSIONS(最大版本间隔),表明版本推进永远落在这个闭区间内。
4.3 figureVersion():墙钟对齐(masterserver.cpp:43)
expectedVersion = now * VERSIONS_PER_SECOND - referenceVersion version = clamp(version + toAdd, expectedVersion ± scaled_bounds)- 让版本号大致与墙钟微秒数对齐(
now * VERSIONS_PER_SECOND - referenceVersion),同时通过MAX_VERSION_RATE_MODIFIER与MAX_VERSION_RATE_OFFSET限制对齐幅度,保证单调性不被破坏; - 该函数在 masterserver.cpp:440-468 有大量单元测试断言(如
figureVersion(1e6, 1.5, 0, 100, 0.1, 1e6) == 1000110),验证了追赶墙钟、回退钳制、超大步进等场景。
4.4 Live Committed Version 追踪
serveLiveCommittedVersion():响应 GRV Proxy 的getLiveCommittedVersion请求与代理的版本报告;updateLiveCommittedVersion():当代理上报新的已提交版本时更新水位;- 启用版本向量(Version Vector)时,额外维护每个 Tag(存储服务器)的提交版本信息(
ssVersionVector),支持 TLog 单播与更细粒度的持久化追踪。
五、Resolver:纯冲突检测
Resolver(fdbserver/resolver/Resolver.cpp)只做一件事:维护已提交写范围的滑动窗口,判定新事务的读范围是否与更新的已提交写范围重叠。它不写 TLog,不做持久化决策。
5.1 Resolver 与 ConflictSet 结构
struct Resolver { int commitProxyCount, resolverCount; NotifiedVersion version; ConflictSet* conflictSet; // SkipList-based conflict tracking IKeyValueStore* txnStateStore; // metadata KVS std::map<NetworkAddress, ProxyRequestsInfo> proxyInfoMap; KeyRangeMap<ServerCacheInfo> keyInfo; };ConflictSet(fdbserver/resolver/ConflictSet.cpp:753):
struct ConflictSet { SkipList versionHistory; // version → conflict data Version oldestVersion; };versionHistory是一个基于 SkipList 的版本化写范围索引:每个版本节点挂载该版本提交的写冲突区间;oldestVersion标记滑动窗口的最老版本,用于空间回收;- 该结构在
ConflictSet.cpp中还有独立的性能测试入口(ConflictSetbenchmark,统计 Build/Add/Detect 等计数)。
5.2 resolveBatch()(Resolver.cpp:263)
- 内存检查:当
totalStateBytes > RESOLVER_STATE_MEMORY_LIMIT(默认 1e6,fdbserver/core/ServerKnobs.cpp:927)时阻塞,防止无界增长; - 版本排序(
versionReady(),Resolver.cpp:226):等待 resolver 的版本推进到prevVersion,保证同一代理的批次按版本严格串行处理; - 冲突检测:
ConflictBatch conflictBatch(self->conflictSet, &reply.conflictingKeyRangeMap); for (auto& txn : req.transactions) conflictBatch.addTransaction(txn, newOldestVersion); conflictBatch.detectConflicts(req.version, newOldestVersion, commitList, &tooOldList);- 对每笔事务:检查其读范围是否与更新版本(大于其读版本)上已提交的写范围重叠;
- 把每笔事务的冲突状态写入
reply.committed[](结合committed位图与conflictingKeyRangeMap报告冲突键);
- 状态事务处理:若启用,应用元数据变更(与代理侧的元数据处理呼应);
- 版本清理:擦除过期状态事务与过老的版本历史,把内存占用限定在滑动窗口内。
5.3 ConflictBatch::detectConflicts()
void detectConflicts(Version now, Version newOldestVersion, std::vector<int>& nonConflicting, std::vector<int>* tooOldTransactions)算法本质(见 ConflictSet.cpp:948):对每笔事务的读冲突区间,在 SkipList 中查找读版本之后提交的写冲突区间,任何重叠即判冲突;同时推进oldestVersion并调用versionHistory.removeBefore()修剪过期节点,用combinedWriteConflictRanges批量插入新版本的写区间,保证检测与清理都是对数级复杂度。
六、Tag 分配:mutation 如何找到存储服务器
Tag 是把 mutation 与存储服务器连接起来的纽带,全链路如下:
- 预解析阶段:对每条 mutation,
tagsForKey(mutation.key)(fdbserver/commitproxy/ProxyCommitData.h:351)返回其 key 对应的 Tag 集合; - Tag 来源:
KeyRangeMap<ServerCacheInfo> keyInfo维护 key 区间 → 存储服务器 → Tag 的映射(由数据分布/迁移持续更新,resolver 与代理都持有类似结构); - 写入 LogPushData:每条 mutation 通过
toCommit.addTags(tags)把 Tag 挂到待推送数据上; - TLog 内部路由:消息按
TagData[tag.locality][tag.id]进入对应队列(TLog 按 locality + id 双层组织队列,见 fdbserver/tlog); - 存储服务器拉取:每个存储服务器只"偷看"(peek)分配给自己的 Tag,拉取相关 mutation 应用到本地。
这一机制使得提交数据天然按 key 分布路由,同时支持副本(同一 Tag 多副本)与版本向量模式下的按 Tag 精确投递。
七、关键文件索引
以下为提交管线各角色的核心实现文件,便于对照本文内容深入阅读:
| 文件 | 用途 |
|---|---|
| fdbserver/commitproxy/CommitProxyServer.cpp | 5 阶段提交批处理管线(CommitBatchContext、preresolutionProcessing/getResolution/postResolution/transactionLogging) |
| fdbserver/commitproxy/ProxyCommitData.h | ProxyCommitData、tagsForKeyTag 查找 |
| fdbserver/grvproxy/GrvProxyServer.cpp | 读版本分配、优先级队列、限速执行、动态批处理 |
| fdbserver/grvproxy/GrvTransactionRateInfo.h | 由 ratekeeper 驱动的令牌桶限速器 |
| fdbserver/sequencer/masterserver.cpp | 版本分配、墙钟对齐(figureVersion)、live committed version |
| fdbserver/sequencer/MasterData.h | MasterData、版本追踪 |
| fdbserver/sequencer/ResolutionBalancer.cpp | resolver 间冲突检测负载均衡 |
| fdbserver/resolver/Resolver.cpp | 冲突检测、批量解析(resolveBatch/versionReady) |
| fdbserver/resolver/ConflictSet.cpp | SkipList 版冲突区间追踪与detectConflicts |
| fdbserver/ratekeeper/Ratekeeper.cpp | 全局 TPS 限额计算(updateRate)与下发(handleGetRateInfoReqs) |
| fdbserver/core/ServerKnobs.cpp | 上述各 Knob(VERSIONS_PER_SECOND、START_TRANSACTION_BATCH_INTERVAL_*、RESOLVER_STATE_MEMORY_LIMIT等)默认值 |
配套的流程示意图可参阅 diagram_05_commit_pipeline.md。若想从系统层面观察这些角色如何被部署与协同,可进一步阅读 fdbserver/SimulatedCluster.cpp 与各角色的 Interface 定义(如fdbserver/include/fdbserver/CommitProxyInterface.h、GrvProxyInterface.h、ResolverInterface.h、MasterInterface.h等)。
结语
从 Commit Proxy 的五阶段批处理,到 GRV Proxy 的令牌桶与优先级淘汰,再到 Master 的墙钟对齐版本号与 Resolver 的 SkipList 冲突窗口,FoundationDB 的提交管线展示了分布式事务引擎在"吞吐"与"一致性"之间的精巧平衡:用批处理摊薄网络往返、用流控闭环吸收存储落后、用严格有序的版本串行化保证正确性。理解这条写路径,是深入诊断提交延迟、优化写入吞吐、乃至阅读 fdbserver/workloads 中各类事务负载测试(如 AtomicWorkload、CommitBuggyWorkload)的前提。
【免费下载链接】foundationdbFoundationDB - the open source, distributed, transactional key-value store项目地址: https://gitcode.com/gh_mirrors/fo/foundationdb
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考