ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

TigerBeetle 架构深度解析:复制状态机、LSM 树森林与面向极限性能的确定性设计

TigerBeetle 架构深度解析:复制状态机、LSM 树森林与面向极限性能的确定性设计 TigerBeetle 架构深度解析复制状态机、LSM 树森林与面向极限性能的确定性设计【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle本文是 TigerBeetle面向任务关键型安全与性能的金融事务数据库内部架构的技术总览。全文以官方架构文档为核心依次拆解其问题陈述、六副本复制状态机的数据通路、数据文件在磁盘上的物理布局以及贯穿全栈的十几项核心设计决策——从静态内存分配、零依赖、确定性到灵活仲裁、协议感知恢复与哈希链——并结合本仓库源码src/vsr、src/lsm、src/constants.zig等给出实现级佐证。读完本文你将理解 TigerBeetle 为何选择单线程 确定性 复制这一组合来同时解决正确性、持久化与极端写竞争下的性能问题并掌握每条设计原则在代码库中的落点。问题陈述TigerBeetle 为哪些工作负载而生TigerBeetle 面向的工作负载具备以下特征竞争激烈且难以并行/分片受 Amdahl 定律限制增加并发反而无益以写为主绝大多数操作是写入而非读取需要非常高的吞吐量与中低延迟需要强一致性保证对持久性要求极高。现阶段 TigerBeetle 聚焦于金融事务转账。每笔转账会触及两个账户而账户服从 Pareto 分布——少数热门账户承担了绝大多数转账。多对象事务叠加高竞争使得传统数据库架构效率低下先锁定两个账户再跨网络把余额送到应用侧计算更新既慢又因竞争而无法高效地分散到多台机器上。TigerBeetle 实现了会计逻辑但底层状态机是可替换的。它做的是复式记账但凡是满足极端竞争下的安全与性能这一相同需求的工作负载它都可以胜任。系统概览从客户端请求到磁盘落地的完整链路六副本集群与复制状态机TigerBeetle 是一个分布式系统一个由六个副本组成的集群。每个副本的全部状态都保存在磁盘上的单个文件中即数据文件详见 数据文件文档。副本通过共识协议详见 VSR 文档保证各自的数据文件内容一致。具体而言TigerBeetle 是一个复制状态机。系统的基态是一条不可变、哈希链式、只追加的 prepare 日志每条 prepare 是一个包含 8000 个独立转账对象的批。主副本primary负责接受客户端请求决定请求的处理顺序将下一条请求转换为一条 prepare将 prepare 追加到预写日志Write Ahead Log, WAL分配下一个序列号并把上一条日志条目的校验和作为指针写入将 prepare 复制到各备份副本。提交路径prepare_ok 与法定人数备份副本收到 prepare 后将其写入数据文件的 WAL 区段并向主副本发送prepare_ok消息。当主副本收集到法定人数的prepare_ok后该 prepare 即视为已提交——此后它不能再从日志中移除或重排。副本按序列号顺序执行已提交的 prepare将一批转账应用到本地状态。由于所有副本从相同的空初始状态出发且状态转移函数是确定性的副本最终收敛到相同状态。若主副本故障共识算法会选出新的主副本后者能正确重建日志的最新状态。从 VSR 协议的实现看见 vsr.md正常协议Normal Protocol的具体步骤是客户端发送request→ 主副本转为prepare分配 op 号与时间戳→ 每个副本并发地写 WAL 转发给链上下一副本→ 各副本回prepare_ok→ 主副本收集到复制法定人数且前面所有 prepare 均已提交时提交 → 主副本回复客户端 → 备份通过后续 prepare 或周期性的commit心跳得知提交。消息类型、来源与目标协议的完整对照表记录在 vsr.md 的命令表格中。数据文件WAL、超级块与网格三个主要区域每个副本把全部数据放进单个.tigerbeetle文件中。数据文件划分为若干区zone主要三个为预写日志WAL、超级块superblock、网格grid详见 数据文件文档。网格Grid占据数据文件的主体可达数 TB是一块 512KiB 等长块的弹性数组。源码中src/constants.zig定义block_size且要求其是扇区大小4096 字节的整数倍src/constants.zig#L584-L590而默认配置config.cluster.block_size 512 * KiB在 src/config.zig#L161 中给出。网格是原始存储层LSM 树等高层数据结构映射到网格物理块上由于系统是确定性的网格中已使用的部分在所有追齐的副本间逐字节一致这使得状态同步与修复可以在网格块粒度上进行。超级块Superblock位于数据文件固定位置是全部状态的逻辑根指针。它引用两块块引用BlockReference{ index: u64, checksum: u128 }二者合起来指定所有 LSM 树的 manifest并引用一个记录空闲网格块的压缩位图free set本身存于网格中。为支持原子更新超级块在磁盘上物理保存4 份拷贝——源码注释明确要求该拷贝数必须为 {4, 6, 8} 等偶数以便配合灵活仲裁src/constants.zig#L540-L549。预写日志WAL环形缓冲区保存尚未进入超级块/网格持久状态的那部分逻辑增量prepare。副本处理 prepare 时写入 WAL → 应用到内存中的当前状态 → 通过分配并写入新网格块应用到待落盘的磁盘状态积累足够多 prepare 后原子地更新超级块使其指向累积到当前的新磁盘状态。有趣的是由于网格实现的是由根指针原子切换的纯函数式持久数据结构经典的 copy-on-write 技术可以把 TigerBeetle 的数据文件整体看作一个文件系统。LSM 树森林与辅助索引系统的导出状态包含两部分不可变的转账只追加日志以及所有账户的当前余额。过去的转账被保留以支持幂等。物理上状态存储为一批 LSM 树构成的森林forest详见 lsm.md。每棵 LSM 树是按某键排序的对象集合例如转账对象存于按唯一时间戳排序的树中以便高效查找。辅助索引树加速其他查找例如有一棵树存转账的借记账户 id 转账时间戳元组并按账户 id 排序据此可查出某账户所有转账的时间戳再据此取回转账对象本身。该森林是磁盘上的函数式数据结构数据组织为磁盘块之树每块 512KiB块之间通过磁盘地址 内容校验和互相引用。从根块即超级块出发其余状态全部可达。检查点、崩溃恢复与物理确定性副本把 prepare 应用到本地状态时从不覆写数据文件中的既有块只写新块。当前超级块状态保存在内存中每隔一段时间被原子刷写到存储形成新检查点。副本崩溃重启后会丢失内存中的超级块于是从存储读取上一个检查点/超级块重放该检查点之后的 prepare 日志后缀来重建丢失状态——确定性保证副本最终精确回到相同状态。逻辑与物理的双重确定性保证了深层哈希链在所有副本上完全一致从而允许对损坏数据进行透明恢复与修复。TigerBeetle 不区分校验和数据的来源是本地磁盘还是另一副本即便出现潜伏扇区错误与螺旋损坏也能保证持久性与可用性。设计决策面向第一性原理的取舍设计中的意图性Intentionality大量软件靠经验反馈回路来演进改一小处、看结果、再回退或加码。TigerBeetle 反其道而行先从第一性原理思考正确方案应当是什么再用实验去证实或证伪心智模型。例如其工程流程本身被显式工程化为一套规范——TIGER_STYLE.md。系统思维作为更大数据处理系统的一部分TigerBeetle 被设计为更大数据处理系统的一环系统外部发生的事与内部同样重要每笔转账携带端到端幂等键由最终应用如手机或网站生成并持久化的唯一 128 位 ID应用不直接向 TigerBeetle 提交转账而是经由API 网关网关为可能不受信任的客户端提供 HTTP API网关把来自不同应用的独立转账聚合为大批量网关无状态、可水平扩展所有状态由 TigerBeetle 管理端到端幂等键保证每笔转账至多处理一次——即便因重试与负载均衡逻辑被路由经过多个网关TigerBeetle 用借贷记账debit-credit模式记录高吞吐业务事务但事务含user_data字段可与通用数据库建立关联相关系统架构见 coding/system-architecture.md。快如哈希表一个不错的思维模型解决金融事务的理想方案是一个内存哈希映射以账户 ID 为键。处理一笔转账就是两次哈希查找、一次余额检查、两次余额更新——这是该问题的光速基准任何其他方案都不可能显著更快。TigerBeetle 在两条轴上超越内存哈希表持久化与高可用内存哈希表一旦重启便数据尽失。TigerBeetle 把数据存在磁盘上掉电安全且数据复制于六个副本即使部分副本宕机集群整体仍可用。大数据集内存哈希表在数据超出 RAM 时失效。TigerBeetle 支持大于内存的数据集要保持最优性能热工作集仍应装入内存。不要浪费耐久性共识算法的功能是把持久性转化为可用性。落在持久存储上的数据是宝贵的应当被充分利用——如果某个副本上单独一块数据位腐烂bit rot不去用集群中其他副本的等价数据修复这块就是浪费。非交互式事务通用目的数据库OLGP见 concepts/oltp.md的主导范式是交互式事务。在通用关系数据库中实现一笔银行转账应用需要开事务 → 从数据库取余额 → 在应用内计算余额更新 → 把更新发回数据库 → 提交。关键在于第 2 步取得的余额锁要到第 5 步才释放——锁跨越了网络往返。当多数事务相互独立如以读为主时这没问题。但对金融记账恰恰相反多数事务是写且因热门账户而相互冲突。因此 TigerBeetle 在数据库内部直接执行事务避免把数据跨网络搬到代码这边。单线程TigerBeetle 是单线程的理由有消极与积极两面。消极理由底层工作负载天然竞争激烈某些账户远比他人热门热门账户间的转账在本质上使系统串行化强行并行事务并不会更快同步开销反而压倒有效工作。借用 Frank McSherry 的观点金融事务处理是一个无限 COST 问题——需要的协调成本随规模增长摊薄后成本趋向无穷。积极理由CPU 相当快单核在满足以下条件时也能轻松扩展到百万级 TPS高效利用核心让它忙于有效工作把其他一切移出热路径做好基本功缓存行对齐的数据结构、内存预取、SIMD 等性能工程 101。此外单线程极大简化了编程模型也让测试高效得多。静态内存分配TigerBeetle 声称不使用malloc/free而是静态内存分配——这是对术语略带个人色彩的使用值得精确界定启动时对系统中每种对象类型依据 CLI 参数计算最坏情况对象数量上界精确分配该数量的对象后进入主事件循环启动后不再创建新对象因此完全不需要动态分配/释放。它与嵌入式系统的真静态分配不同TigerBeetle 不用全局静态区.bss内存用量取决于运行时 CLI 参数。它也与 arena 分配不同arena 在超限时报 out of memory只保证有界、不保证预留足够内存而 TigerBeetle 的内存用量是一切皆有显式上界的推论。知道上界保证了系统在过载时仍能正确运行。例如TigerBeetle 不需要显式的背压backpressure代码——既然一切都有上限就没有什么会无界增长背压来自整个组件体系相互遵守对方上限这一事实。静态上限还带来跑道并发runway concurrency现象高并发应用中并发任务数往往超过底层资源数导致超额订阅通常表现为堆上分配的闭包注册到事件循环如Boxdyn Future。TigerBeetle 无法在运行时堆分配每个future都是一个显式结构体作为拥有它的组件的字段静态分配——在途并发任务的数量天然有上限。一句话总结静态分配是一套确保一切皆有上限的强制力而这些上限是自然涌现的结果。经验表明它极大简化了系统前期思考更多但初始设计完成后各部分往往自动契合并让系统更快、显著降低延迟抖动——这同样是知道上限与底层物理的结果但并非选择静态分配的首要原因。无依赖TigerBeetle 刻意避免依赖仅依赖Linux 内核 API特别是 io_uring让硬件干活Zig 编译器把人类可读源码编译为机器码Zig 标准库的若干部分提供排序、哈希表等基本组件。依赖的用处通常与项目寿命成反比对长寿项目而言控制更多底层活动部件是合理的。TigerBeetle 显式地为长期运行而设计今天就开始搭建全部必要基础设施。这也消除妥协的诱惑——正因为写了栈的大部分静态分配得以贯穿全栈。无依赖还作为保持代码简单易懂的强制力多余的复杂度无法藏在花哨 API 后面。事实证明数据库中的大部分代码并没有那么难。为什么是 Zig由静态分配一节可知TigerBeetle 不需要垃圾回收器。虽可在 GC 语言里做静态分配但更难保证零分配这大幅收窄了语言选择范围——剩下的候选中 Zig 最合适Rust 是紧随其后的竞争者。Zig 与 Rust 都提供空间内存安全Rust 的时序与线程安全更好但静态分配 单线程降低了这些优势的相对重要性单纯的内存安全对 TigerBeetle 只是低门槛一般正确性是入场券全面的测试策略已不给靠 Rust 类型系统兜底留多少空间Zig 的最大优势是表达力与语言复杂度的比值它能以很低抽象层次、一阶代码直接干活易于编写和调试性能导向代码其comptime特性让直接表达想让计算机做什么非常容易代价是缺少声明侧接口但在零依赖语境下不那么重要Zig 对布局、对齐、填充有极佳控制对齐携带指针类型可防微妙错误惯用集合不在构造函数里传分配器而是显式传给需要分配的方法——与 TigerBeetle 的内存管理策略完美契合可用的交叉编译与直接无 glibc内核绑定也帮助压减依赖。确定性高于静态分配的元原则是确定性相同输入给出相同逻辑结果且经由相同物理路径抵达。TigerBeetle 中一切皆确定收益体现在多处简化复制状态机实现把同步可变状态降维成同步不可变、只追加、哈希链日志这一更简单的问题允许物理修复而非逻辑修复若某棵 LSM 树存储的一个字节损坏逻辑一致性下修复可能要重传整棵树慢且可能失败若副本还保证磁盘上字节相同则只需传输含该字节的单个磁盘块。TigerBeetle 即此方案保证集群内副本收敛到逐字节一致的 LSM 树结构物理修复大幅简化错误处理读数据的函数没有错误分支它总是拿到带所需校验和的块最多是透明地从别的副本读物理确定性要求 LSM 压缩工作确定性调度压缩工作均匀铺开从而界定了最坏情况延迟确定性让随机化测试如虎添翼任何测试失败都可通过分享导致失败的种子可靠复现。仿真测试VOPRTigerBeetle 用从示例测试到严格风格指南的多种技术保证正确性其中最重要的是仿真测试Deterministic Simulation Testing详见 vopr.md。仿真器 VOPRTheViewstampedOperationReplicator名字受电影《战争游戏》中的超级计算机 WOPR 启发能在单线程上运行整个集群注入各类存储故障并把时间无限加速。VOPR 把智能工作负载生成器、群体测试swarm testing与上千个 CPU 核心结合起来轻易覆盖系统全部可能行为。其关键价值在于与形式化证明和模型检查不同仿真测试检验的是具体实现——TLA 之类的工具擅长调试算法本身却难以验证代码是否正确地实现了算法或验证底层假设。仿真中系统所有不确定部分时钟、网络、磁盘都被打桩随机种子调优故障注入参数丢包、重排、网络分区、磁盘读写损坏。任何失败都能用种子 Git 提交哈希精确重放。配套地代码库中散布着成千上万条断言而且 TigerBeetle 在生产环境也保留断言——逻辑是宁可停止运行也不在错误状态下继续运行仿真器还叠加了额外检查器例如验证各副本数据文件逐字节一致。机械同理心网络、存储、内存、CPU机械同理心是指虽然 CPU 是通用设备什么都能算但常可把某算法重新表述为对 CPU 友好的形态从而大幅提速。小细节对速度影响极大有时小细节还主导了更大的架构。TigerBeetle 的机械同理心覆盖计算的四种主色网络带宽有限若某节点需求远高于其余节点网络将被闲置。因此主副本不向所有备份广播 prepare而是只发给两个备份由备份接力转发这正是 VSR 正常协议的链式结构见 vsr.md 中的时序图。存储通常能维持多路并行 IOTigerBeetle 尽量使其饱和例如压缩需要从磁盘读表时会一次入队多个块读取同时避免超额订阅对并发 IO 数设上限对应源码grid_iops_read_max/grid_iops_write_max等参数见 src/constants.zig#L525-L528。内存并不快错过缓存命中内存的操作慢一个量级。TigerBeetle 的数据结构紧凑以最大化缓存命中率并按缓存行组织——单次操作所需全部数据可在一条缓存行内找到而非散落内存例如 Transfer 对象的大小就是两条缓存行。CPU擅长冲刺胜过跑酷直线代码飞快、SIMD 能一次扫多条通道而if尤其不可预测的if会拖慢它。处理事件时每个事件是新建账户或新转账TigerBeetle 把分支上提事件成批到来且每批同质要么全是转账、要么全是账户事件种类分支被移出内层循环。批处理最有效的优化原则之一是摊销开销若必须做高代价操作就让其结果为许多小的有用操作买单。TigerBeetle 在多个层面使用批处理单笔转账聚合为一条 prepare摊销复制与预取 IO 开销32 条 prepare 的变更先在内存聚合再一起写盘1024 条 prepare 的变更聚合后原子覆写超级块推进检查点LSM 树基本以表与值块为单位运作每者聚合大量单条记录。LSM 树与森林TigerBeetle 把数据在磁盘上组织为一组日志结构合并树LSM。对其工作负载而言 LSM 有几个诱人特性特别擅长写而 TigerBeetle 是写密集型以数据批次为组织单位与 TigerBeetle 其余部分一致热数据靠近根、冷数据沉到低层恰好匹配 Pareto 分布的工作负载。常见数据库中整库就是一棵 LSM 树、表靠键前缀逻辑构造TigerBeetle 不然——它使用许多棵独立的树LSM 森林每棵树存定长值例如转账对象树的值是 128 字节id 树的值是 32 字节u128id u64时间戳 u64填充。每棵树可针对特定值大小特化提升性能与存储效率Zig 的comptime让通过元编程配置 LSM 森林格外容易。从 lsm.md 可知树的三类表每个树一个内存可变表所有更新直接作用其上、一个内存不可变表可变表内容周期性移入并刷到第 0 层、以及第 0 层到lsm_levels - 1通常 7 层逐层指数递增的磁盘表层内表数可达lsm_growth_factor ^ (level 1)增长因子通常为 8。压缩以节拍/小节beat/bar为节奏增量进行一次完整压缩阶段称一小节含lsm_compaction_ops拍每拍在每次提交后异步执行偶/奇层分占前后半小节同一时刻最多⌈levels/2⌉个压缩并发选择与父层重叠可见表最少的表压缩与父层最少重叠策略。另外还有快照snapshot机制控制压缩输出表何时可见、输入表何时可删以及记录表增删的 manifest 日志src/lsm/manifest.zig 与其上构建的 src/lsm/manifest_level.zig、src/lsm/table_memory.zig 是这些结构的实现所在。网格Grid数据文件的主体组织为均匀的等尺寸块网格每块 512KiB。虽然 LSM 树按类型特化它们都使用同一网格ManifestLog等辅助持久数据结构也构建于网格之上。每个网格块同时也是一个合法的网络Message见下文消息传递且被哈希链链接并在副本间确定性一致——这使物理修复成为可能块损坏时用其校验和透明地向对等副本请求数据。控制平面 / 数据平面分离批处理是更一般原则的特例控制平面与数据平面分离。类比喷气机驾驶舱里各种仪表拨杆按钮控制引擎数据平面飞行员不亲自出苦力只指挥引擎出力。TigerBeetle 中决定应用哪条 prepare是控制平面逐笔处理转账是数据平面一般控制平面是 O(1)数据平面是 O(N)。两种模式必须分开把控制平面的if留在数据平面的for之外内层循环才能无分支性能受益反过来控制平面开销小可以肆无忌惮地加断言甚至花 O(N) 时间去验证 O(1) 操作。同步执行与预取状态机的commit函数真正实现复式记账的函数位于 src/state_machine.zig是同步的它接收转账切片在每个转账的紧凑单 CPU 循环里判定是否可应用计算目标余额变更并把结果写进输出切片。关键在于commit本身不做任何存储读取——这是性能的钥匙。所有 IO 发生在独立的预取prefetch阶段给定一批转账无需实际执行就能预测需要取哪些账户而 commit 执行必须串行所有预取 IO 却可并行。拥抱并发TigerBeetle 用顺序执行来获得简单且高性能的严格可串行化语义但这不意味着一切都要串行——上一节已展示可以同时并行地从存储取数据、串行地在 CPU 上应用复式记账规则。这个模式可以推广TigerBeetle 拥抱并发顺序执行才是例外VSR 协议中 prepare 并发复制主副本收到请求、分配 op 号后立即启动复制循环不等复制完成就开始下一条请求请求的执行必须串行但复制可以并发对同一条新 prepare主副本并发地写入本地存储、启动复制循环。prepare 在主副本收到来自一批副本的prepare_ok法定人数后即提交该法定人数不必包含主副本——主副本甚至可以在 WAL 还在写该消息时并发地执行它LSM 压缩同样是流水线双向归并本身需串行但可同时从磁盘取多个块更进一步可让读块、内存归并、写结果块三者并发。io_uringTigerBeetle 只使用 io_uring 做 IO它把批处理与并发完美结合。微观层面TigerBeetle 的代码不适合协程或线程——并发粒度细到单次系统调用级别例如流水线压缩的自然写法就是为两个层各发一条并发读 syscall这几乎就是 io_uring 暴露给程序员的接口。io_uring 也是 Linux 上真正异步磁盘 IO 的合理途径且吞吐更好但这些是次要的——主要原因是接口与问题天然契合。io_uring 与静态分配有有趣的交互任何异步操作都需在某处保存恢复上下文通常堆分配但这与静态分配冲突。TigerBeetle 中每个组件自行保存恢复上下文例如 Journal 持有固定大小的Write结构数组含回调上下文各组件的上下文由IO组织成单条侵入式链表见 src/io.zig 与 Linux 实现 src/io/linux.zig。于是IO可管理任意多在途 IO 而无硬编码上界但总并发量仍有限——该限制是隐式的即各组件显式上限之和。时间记账业务逻辑需要墙上时钟做转账超时。状态机不能直接调 OS 取时间——那会破坏确定性。因此 TigerBeetle 主副本在把请求转成 prepare 时把具体时间戳注入状态机逻辑。集群时间的最终来源是 NTP。潜在故障模式是主副本与 NTP 服务器隔离为确认主副本时钟在可接受误差内集群聚合复制法定人数的计时信息并保证状态机观察到的时间严格单调递增。这一高质量时间实现既服务业务逻辑超时也在内部扮演关键角色每个对象都有全局唯一的u64创建时间戳充当合成的自然主键。直接 IO绕过页缓存TigerBeetle 绕过操作系统页缓存使用直接 IODirect IO。普通应用写文件时 OS 只更新内存缓存数据稍后才落盘——这对绝大多数应用是良好默认但 TigerBeetle 例外它指示 OS 直接读写磁盘、绕过一切缓存。绕过页缓存首先是正确性所需fsync虽能把页缓存刷盘却不能可靠处理错误。其次符合避免依赖、减少假设的原则TigerBeetle 既不需要 OS 提供页缓存也不依赖文件系统——它只用单个文件因此可以直接跑在裸块设备上。灵活仲裁TigerBeetle 集群有个特点官方建议使用偶数 6 个副本。通常共识实现用2f 1个节点3 或 5以得到明确多数TigerBeetle 则用灵活仲裁Flexible Quorums6 副本下复制法定人数仅为3——集群一半副本把 prepare 持久化到磁盘它就被视为逻辑提交而变更视图轮换主副本角色至少需要4个副本。因为任意 3 子集与任意 4 子集必然相交新主副本保证知道所有可能已提交的 prepare。推论只要有 3 个副本含主副本在线集群即可保持可用。6 副本集群若同时崩溃 3 个仍有 50% 概率保持可用逐个崩溃则概率更高——剩 4 副本时下一个崩溃者是主副本的概率仅 25%。源码层面src/config.zig提供quorum_replication_max: u8 3src/config.zig#L157src/constants.zig的注释点明按 Flexible Paxos只要满足quorum_replication quorum_view_change replicas就可以把视图变更仲裁提到多数以上、把复制仲裁降到多数以下以优化常态路径src/constants.zig#L122-L131。不同副本数下的复制/视图变更/NACK 仲裁数值表见 vsr.md 的仲裁小节。协议感知恢复TigerBeetle 假设磁盘可能失效数据即使当初write和fsync都成功也可能最终物理丢失此时它用其他副本上的相同数据透明修复故障扇区。但故障存储无法被存储接口完全封装需要共识协作解决。考虑这个反例主副本接受客户端请求、分配 op 号转成 prepare 并开始复制复制期间它成功把 prepare 追加到本地 WAL却未能广播给其他副本。随后主副本重启集群切换到新主副本而这条 prepare 在原主副本的 WAL 里损坏了。由于该请求已被 prepare本应进入新视图但集群里只有这一份拷贝且已损坏便无法执行。难点在于可能已提交potentially committed的 prepare 未必复制到完整复制法定人数其持久性被削弱损坏时需要特殊处理——方案是NACK 协议。视图变更期间参与副本可以明确声明从未接受过某条 prepare若 6 个副本中至少 4 个 NACK 该 prepare即可推断它从未被完整复制从而安全丢弃——即便它已损坏。这一思路对应 VSR 文档中的 nack 术语与视图变更流程见 vsr.md 的视图变更协议。哈希链与批处理普遍提升性能类似对安全性的普遍提升是哈希链为每个数据单元计算校验和把该校验和并入某个同样被校验的父数据。这与 git commit 的结构相同只是应用得更广泛。哈希链绑定意图知道校验和后收到任何数据都能强保证这正是要找的数据既防硬件错误与损坏也防导致值混淆的编程错误。TigerBeetle 哈希链覆盖块BlocksLSM 是块之函数树父块索引块含指向子块值块的指针——即u64块地址 u128校验和的配对磁盘上以结构体数组SoA形式存储索引块的指针存于 manifest 日志块。副本读块时已知道其校验和校验和存在块外这能防误向 IO磁盘把正确数据写到错误偏移的一种故障仅靠内部校验和无法发现外部校验和还使透明修复成为可能——本地读不到块时不报错而是用校验和作标识自动请求其他副本发送。哈希链根部的根校验和存于超级块超级块自身物理复制 4 份以防损坏且每个新版本都包含上一版本的校验和——两版本同时存在时顺序弱受序列号约束、强受哈希链约束。prepareprepare 消息复制、WAL、共识的单位被哈希链连接为相邻两条 prepare 提供强顺序保证并经传递闭包给出全局一致性保证——从历史起点到最新 prepare 的整条序列都有效。这改善了 WAL 修复备份需格外小心罕见视图变更时还需主副本一条额外消息确保日志中最新 prepare 正确而其前的任何 prepare 都可沿哈希链修复。请求每条客户端请求都包含上一条请求回复的校验和。这些校验和对数据校验没那么关键但强证明请求顺序得到遵守。消息传递弱传输契约TigerBeetle 对底层传输协议无感只要求非常弱的消息传递语义消息可能被丢弃、重复、重排以及非拜占庭式损坏。虽然具体MessageBus不在 VOPR 中测试MessageBus的缺陷也不太可能影响正确性——因为传输层契约被刻意设计得非常弱消息类型矩阵见 vsr.md 的命令表格。星型复制由分布式计算的八大谬误可知网络拓扑随时可能变化复制必须容忍延迟波动与故障。TigerBeetle 采用星型复制主副本并行向所有其他副本发送每条 prepare。结合灵活仲裁主副本只需等 5 个备份中最快两个的应答使复制既延迟容忍又对单节点故障有韧性。在 VSR 正常协议的实现src/vsr/replica.zig中prepare 沿主副本 → 备份链转发并收集prepare_ok正是星型复制与链式转发的结合。各设计支柱在源码中的落点把上述设计决策映射到仓库便于读者按图索骥设计支柱主要源码位置关联文档复制状态机与 VSR 协议src/vsr/replica.zig、src/vsr/journal.zigvsr.md数据文件超级块/网格/WALsrc/vsr/superblock.zig、src/vsr/grid.zigdata_file.mdLSM 森林src/lsm/forest.zig、src/lsm/groove.zig、src/lsm/manifest.ziglsm.md状态机复式记账src/state_machine.zig数据建模常数与仲裁配置src/constants.zig、src/config.zigvsr.md确定性仿真测试VOPRsrc/vopr.zig、src/testing/clustervopr.mdIOio_uring 与直接 IOsrc/io.zig、src/io/linux.zig—时钟NTP 聚合、单调递增src/vsr/clock.zig、src/time.zigtime.md 相关章节结论TigerBeetle 的目标是以任务关键级安全与高吞吐性能支撑全球事务。当前聚焦金融事务它在世界变得愈加事务化、实时化的背景下从根本求解了规模化的低成本 OLTP 难题。其答案并非某一项魔法而是问题陈述写密集 高竞争→ 非交互事务 → 单线程 → 静态内存分配 → 确定性 → 复制与物理修复 → 哈希链与灵活仲裁 → 机械同理心与批处理这一整条环环相扣的设计链每条决策都以其他决策为前提共同把正确性与性能统一在确定性这一元原则之下。延伸阅读若想继续深入仓库内相关文档按阅读顺序推荐VSR 共识协议详解正常协议、视图变更、WAL/网格修复协议与仲裁数值表数据文件布局WAL、超级块、网格三区域如何协作表示持久状态附具体 Zig 结构体LSM 树与森林表、压缩小节/节拍调度、快照可见性与 manifest 的完整机制确定性仿真测试VOPR 如何用种子重放故障注入与检查器验证工程风格规范作为意图性设计的具体产物规定代码的编写方式数据建模 与 系统架构从应用侧理解幂等键、网关与批量语义。这些设计也深受外部思想启发LMAX 的机械同理心实践、Viewstamped Replication 论文、Flexible Paxos 的仲裁相交理论、Protocol-Aware Recovery 的磁盘损坏处理思路以及 FoundationDB/Antithesis 的确定性仿真测试方法论。读者可在原架构文档的参考文献部分按图索骥回到第一性原理校验本文的每一处取舍。【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表