土法炼钢兴趣小组的算法知识备份

【FoundationDB 内核】严格可串行化与 5 秒限制:实时顺序与恢复有界性

文章导航

分类入口
databasestorage
标签入口
#foundationdb#strict-serializability#mvcc#five-second-limit#recovery#occ#external-consistency#sigmod2021

目录

第 8 篇 说明了冲突如何被检测;本篇回答更强的问题:检测通过之后,全局顺序是否不仅可串行化,还与客户端感知的实时顺序一致——以及为何系统用硬性的约 5 秒事务上限换取恢复与元数据有界。

Developer Guide 写:FoundationDB 提供带外部一致性(external consistency)的可串行化事务。SIGMOD 2021 摘要与 §2.4.2 使用 strict serializability。Known Limitations 则把「超过五秒的长事务」列为设计限制,并指向 Anti-Features 哲学:核心刻意不做某些事,以便把正确性与运维问题关进有界盒子。

本文是「FoundationDB 内核」系列第 9 篇(共 18 篇)。上一篇Resolver 与 OCC下一篇Transaction Log→ 系列目录

版本锚定:FoundationDB 7.x。不把论文或他文中的延迟/吞吐表写成通用结论;本系列实验台账中超时边界实验若未执行,正文只陈述官方错误语义。


一、可串行化、严格可串行化与实时顺序

1.1 三层含义

术语 直观含义
可串行化(serializability) 并发执行的结果等同于某一串行次序
严格可串行化 / 外部一致 该串行次序还尊重实时:若事务 \(A\) 在实时上完全结束后 \(B\) 才开始,则串行序中 \(A\)\(B\)
快照隔离(SI) 读一致快照,但允许写偏斜等异常

FDB 的默认目标是第一行加第二行。SIGMOD 2021 §2.4.2 的论证链是:

  1. Sequencer 为事务分配 read version 与 commit version;
  2. read version \(\ge\) 开始时已知的最大 commit version;
  3. commit version \(>\) 一切已有 read/commit version;
  4. commit version 定义串行历史(兼作 LSN);
  5. 事务在 read version 上所见包含此前已提交写的结果,且 OCC 保证读集在提交前未被并发写破坏。

因此「等价于按 commit version 排序的串行执行」,并且后开始的事务拿不到更旧的全局提交点作为自己的读点——这把实时顺序嵌进版本分配。

1.2 外部一致的反例思维

若系统只保证普通可串行化,允许如下实时序:客户端 \(C_1\) 提交 \(A\) 并返回成功;墙钟之后 \(C_2\) 才开始事务 \(B\),但 \(B\) 的等价串行位置仍可能插在 \(A\) 之前——只要最终状态像某次串行执行。严格可串行化禁止这种「时间倒流」的等价类。FDB 用「新事务 read version 不低于已知最大提交」堵住该洞;代价是所有写共享版本推进与世代健康。


1.3 客户端如何「看见」外部一致

Architecture Determine Read Version:GRV Proxy 向 Master 取最大已提交版本,并确认满足复制策略的一组 transaction log 仍存活(避免旧世代 Proxy 在恢复后继续发 read version)。客户端因此获得的 \(r\) 高于它(通过集群)应已知的提交。跨进程「侧信道」若绕过数据库传递版本,文档仍要求 read version 不低于客户端已知的提交——这是外部一致的客户端义务与系统机制的交接面。

flowchart TD
  A["Txn A commits at version c_A"] --> B["Master tracks max committed"]
  B --> C["Later Txn B requests read version"]
  C --> D["GRV Proxy: r_B >= c_A"]
  D --> E["B reads snapshot at r_B"]
  E --> F["B commits at c_B > r_B"]

若 B 在实时上于 A 成功返回之后才开始,却读到不含 A 的快照,就破坏严格可串行化。FDB 用中心版本与 GRV 路径堵住这条缝;代价是写路径依赖 Sequencer / Master 世代健康(第 6、13 篇)。


二、OCC + MVCC:如何凑成 SSI 叙事

论文称 FDB 通过 OCC 与 MVCC 的组合实现 Serializable Snapshot Isolation(SSI)语义,并达到 strict serializability。读侧:Storage 保留短窗口多版本,按 read version 取快照。写侧:不持锁,提交时验证。与「先拿 commit 时间戳再验证」或「验证后再戳时间戳」的变体相比,FDB 由 Sequencer 定 commit version,做冲突检测,以便批处理版本分配与验证(§2.4.2)。

Snapshot read 是显式出口:同一事务内可以放弃部分读的严格性以减少冲突。默认路径不走这个出口。Atomic operations 则通过「提交时才逻辑读」消除一类 read-write 自冲突,但不削弱对其它事务读该 key 的保护。


2.1 先定版本再验证的含义

先分配 commit version 再做 OCC,意味着冲突检测看到的是已经排进全局序的候选事务。失败的事务仍占用过一个 commit version 号,但不会把写集提交进 TLog(Proxy 在冲突后直接 not_committed)。版本号稀疏前进是正常的;监控「版本速率」时不要把它直接当成成功 TPS。

与某些「验证通过后再贴时间戳」的 OCC 变体相比,FDB 的选择更利于 Proxy 批处理:一批事务共享一次 Sequencer 往返,再并行或流水进入 Resolver。代价是假阳性与版本空洞需要靠短窗口消化,而不是靠精细的逻辑锁表。

三、5 秒限制:不是疏忽,是有界性工具

3.1 官方语义

Known Limitations Long running transactions

这与 Resolver 只保留约 5 秒写历史、Storage 保留约 5 秒可变版本(Architecture: Storage Servers)对齐。客户端必须在窗口内完成「读—计算—提交」,或把大逻辑拆成多个短事务(Known Limitations 给出分层 versioning、间接交换等 workaround 方向)。

3.2 与恢复有界性的关系

SIGMOD 2021 设计原则 Fail fast and recover fast:把故障收敛到统一恢复路径,并追求短 MTTR。§2.4.4:恢复需确定 Recovery Version(RV),并把旧世代日志中有界后缀拷到新 TLog;论文写明该范围「只包含几秒日志」。5 秒窗口使:

Architecture 还写:恢复会把时间快进约 90 秒,使进行中的客户端事务因 transaction_too_old 失败并在重试时找到新世代。这是用客户端可观察错误换取世代切换清晰,而不是悄悄续上旧 Proxy。

flowchart LR
  subgraph Bound["Bounded 5s window"]
    R["Resolver write history"]
    M["Storage in-memory versions"]
    L["TLog suffix to copy on recovery"]
  end
  Bound --> Rec["Recovery stays time-bounded"]
  Long["Unbounded long txn"] -.->|would expand| Bound

distributed/39 已概括「5 秒限制服务恢复有界」这一设计意图;本篇用论文恢复章节与官方 Limitations 把它钉成机制,而不是产品口号。

3.3 10 MB 与其它硬限制(边界)

Known Limitations:单事务受影响数据不超过 10,000,000 字节;key ≤ 10 KB,value ≤ 100 KB。它们限制的是冲突检测与日志体积,与 5 秒一起构成「事务必须小而短」的契约。本篇不展开数据建模技巧,只标明:严格可串行化保证落在遵守这些契约的事务上。

3.4 Anti-Features 哲学上的位置

FoundationDB 文档把部分「不做」写成 Anti-Features:核心保持最小,把数据模型推到 Layer。长事务落在同一哲学:允许任意长读写交互会迫使 Resolver/MVCC/恢复状态无界,从而侵蚀「故障即恢复、恢复必快」的假设。5 秒因此不是单独抠出来的魔法常数宣传,而是与有界状态一致的产品选择;若业务必须分钟级快照,Known Limitations 建议在 Layer 做版本化,而不是要求核心取消窗口。


从选型角度预先回答一个常见问题:若业务天然是高冲突单行更新,FDB 默认 OCC 是否「选错」?答案不是绝对否——原子操作、打散 key、Layer 侧队列化都能把冲突率压回去——但若这些改造成本高于换用悲观模型或分片锁系统,应在第 18 篇的对照框架里显式权衡,而不是指望调一个 FoundationDB 旋钮恢复严格可串行化下的高争用吞吐。本篇把问题留在开放区,避免假装有通用阈值。

3.5 与 10 MB 限制一起读的「小事务」契约

时间上限与体积上限经常被分开吐槽,但它们服务同一目标:让冲突历史、日志扇出与恢复拷贝保持可预测。一个 4 秒内写满接近 10 MB 的事务,与一个读很多 key、几乎写满 5 秒窗口的事务,都会把 Resolver 与 TLog 推向压力区。Known Limitations 建议:单事务受影响数据超过约 1 MB 就应重新设计——这是可用性提示,不是硬错误阈值宣传。Layer 设计(Record Layer 索引维护等)必须把二级索引更新计入同一事务的体积预算,否则会在「业务看起来只改一行」时撞上限。

四、高冲突:开放问题而不是调参口号

论文坦承 OCC 不保证事务一定能提交,并依赖生产冲突率很低。Developer Guide Minimizing conflicts 给出工程处方:打散热点、原子操作、并行读以缩短窗口、谨慎使用 snapshot。

仍开放的是定量边界

本系列不提供未跑实验的曲线。选型收束见 第 18 篇;排障决策树见 第 17 篇

stateDiagram-v2
  [*] --> Running: read version granted
  Running --> Committing: client commit
  Committing --> Committed: OCC pass + TLog durable
  Committing --> Retry: not_committed
  Running --> AbortOld: transaction_too_old
  Retry --> Running: client restarts txn
  AbortOld --> Running: client restarts txn
  Committed --> [*]

高冲突时状态机在 Retry 上打转:每一次重试都重新取 read version、重新读、重新提交。有效工作随冲突率近似按 \((1-p)^n\) 衰减(\(p\) 为单次冲突概率,\(n\) 为逻辑操作被拆成的事务次数)——这是定性模型,不是拟合曲线。缓解手段改变的是 \(p\) 与窗口长度,而不是取消严格可串行化本身。

第 8 篇 的假阳性叠加时,应用看到的「冲突」可能高于真实数据依赖;排查时应区分热点 key、过大 read range、以及 Resolver 分片边界,而不是先怪 Sequencer「太慢」。


五、与 snapshot read / 原子操作的边界

严格可串行化是默认路径。两条官方提供的削弱或绕开局部冲突的机制,常被误读成「系统其实是 SI」:

  1. Snapshot read:该次读不进入 read conflict range,并发写可改变你「本应」保护的集合;文档要求你用显式 conflict range 补回真正在意的 key。
  2. Atomic operations:逻辑上含读,但不把当前值暴露给客户端,冲突语义接近「只写」;适合计数器,不适合「读出旧值做复杂分支」却仍想零冲突。

两者都不改变 Sequencer 版本的全序,也不取消 TLog 提交点;它们只移动 OCC 检查的集合。第 8 篇已拆 API;本篇强调:讨论严格可串行化时,必须声明事务是否全程使用默认读。

另外,只读事务不访问 Resolver、可本地结束(论文 §2.4.1),其「串行点」就是 read version 上的快照。它们享受 MVCC 的无锁读,却仍受 5 秒窗口约束——长只读同样会 transaction_too_old


六、学术谱系、工程间隙与开放问题

谱系:严格可串行化 / linearizability 与数据库隔离的交叉长期由 Herlihy & Wing(linearizability)、以及数据库隔离文献(Adya、Berenson 等)共同刻画。FDB 选择「全局 Sequencer 版本 + OCC」而不是每分片 Raft 排序 + 两阶段锁,是对低争用 OLTP 与快速恢复的押注。与 Spanner 的 TrueTime 提交等待相比,FDB 不用不确定性窗口等待来换外部一致,而是用中心版本与短事务窗口——假设与故障模型都不同,不能直接比谁「更强」。

工程间隙:论文评价章节含生产延迟与重配置时间,但那些数字绑定特定集群与时期。把「严格可串行化」理解成「任意长、任意大事务的免费午餐」,会在 Limitations 上撞墙——这是文档已写明的产品边界,不是实现 bug。

开放问题:云盘尾延迟与 Storage 多版本窗口相互作用时,客户端是否更频繁撞 transaction_too_old,从而在「强隔离」下被迫把业务拆成弱一些的多事务协议?这需要受控实验;本篇只把问题立住。


七、常见误解

误解 事实
「可串行化已经包含实时顺序」 普通可串行化允许与实时相反的等价串行序;严格可串行化才绑实时
「5 秒是临时缺陷」 Known Limitations 将其归为设计限制;服务于有界 MVCC/恢复
「严格可串行化所以可以长事务」 恰恰相反:强保证落在短事务契约上
「恢复快进 90 秒会丢已提交数据」 快进使旧事务报错重试;已 durable 提交由恢复版本协议保留(第 10、13 篇)
「SI 与严格可串行化只是名字不同」 SI 允许写偏斜等;FDB 默认读集参与冲突以挡住这类现象
「开了 snapshot 仍声称全程严格可串行化」 Snapshot 局部削弱隔离;需在文档与代码审查中显式标记

八、小结

三句话小结

  1. Sequencer 版本定义串行历史,GRV 路径保证新事务的读点不低于已知提交,OCC 保证读集有效,合起来对齐严格可串行化。
  2. 约 5 秒上限与 Resolver/Storage 的有界多版本窗口一致,使恢复拷贝的日志后缀与冲突状态保持秒级有界。
  3. 高冲突下 OCC 重试放大仍是开放的工程问题;处方是减读集与改数据模型,而不是指望取消时间上限。

下一篇进入 Transaction Log:复制、durable version、提交点,以及与 Storage 异步消费的解耦。


上一篇Resolver 与 OCC

下一篇Transaction Log:提交点与持久化

返回 系列目录


写偏斜对照实验若要在本系列补做,应同时跑「默认读」与「snapshot 读」两组,并记录冲突错误码分布;在实验台账完成前,正文只保留机制判断。

文末核对提示

本节补记:机制结论以 SIGMOD 2021 与 FoundationDB 7.x 官方 Architecture / Developer Guide / Known Limitations 为准;若源码字段名随小版本重命名,以对应 tag 的 fdbserver 实现为准,不把过时幻灯片当契约。

阅读本篇时建议同时打开系列 index 中的依赖图:本篇上承写路径与版本分配,下接恢复与排障;不要孤立地把单角色文档当成完整正确性证明。

本节补记:机制结论以 SIGMOD 2021 与 FoundationDB 7.x 官方 Architecture / Developer Guide / Known Limitations 为准;若源码字段名随小版本重命名,以对应 tag 的 fdbserver 实现为准,不把过时幻灯片当契约。

阅读本篇时建议同时打开系列 index 中的依赖图:本篇上承写路径与版本分配,下接恢复与排障;不要孤立地把单角色文档当成完整正确性证明。

参考资料

论文(A 级)

  1. Zhou et al. FoundationDB…. SIGMOD 2021(§2.4.2 严格可串行化、§2.4.4 恢复与 RV、设计原则 Fail fast)。
  2. Kung & Robinson. TODS 1981(OCC)。
  3. Berenson et al. A Critique of ANSI SQL Isolation Levels. SIGMOD 1995(隔离现象;对照 SI)。
  4. Herlihy, Maurice, Wing, Jeannette. Linearizability: A Correctness Condition for Concurrent Objects. TOPLAS 1990(外部一致 / 线性相关术语谱系)。

官方文档(A 级)

  1. FoundationDB 7.x, Developer Guide — 严格可串行化 / 外部一致性表述、snapshot 与原子操作。
  2. FoundationDB 7.x, Known Limitations — Long running transactions、事务大小。
  3. FoundationDB 7.x, Architecture — Determine Read Version、Resolvers、Recovery 快进。
  4. FoundationDB, Anti-Features — 核心最小集哲学(与长事务限制对照阅读)。

站内对读

  1. 第 8 篇 Resolver第 10 篇 TLog第 13 篇恢复
  2. distributed/39(5 秒限制的选型表述)。

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。


By .