TigerBeetle 的 OLTP 设计:从业务交易实时记录到每秒百万级吞吐的数据库 TigerBeetle 的 OLTP 设计从业务交易实时记录到每秒百万级吞吐的数据库【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle导读本文以 TigerBeetle 官方概念文档 docs/concepts/oltp.md 为核心脉络系统讲解 OLTP联机事务处理的领域特征——实时记录业务交易、写密集型负载、热账户争用与分片困境——以及 TigerBeetle 如何从底层针对这些特征进行设计无行锁的强一致性、单核架构、批量处理等。读完本文你将理解为什么业务交易数据库不应该用通用 SQL 数据库硬扛并掌握 TigerBeetle 作为 OLTP 系统核心的定位以及它与 OLGP 数据库如何协作分工。OLTPOnline Transaction Processing联机事务处理的核心是实时记录业务交易支付、销售、共享出行订单、游戏分数、API 调用计费……这些每秒都在真实世界里发生、必须以极低延迟持久化的事件构成了一个经济体最核心的数据流。而 TigerBeetle 正是一个专门为这种 OLTP 领域设计的金融事务数据库其官方定位是为任务关键型安全与性能而设计。OLTP 是什么记录业务交易而非查询业务数据理解 TigerBeetle 的起点是区分两种截然不同的数据库工作负载OLTPOnline Transaction Processing关注把业务交易实时写进数据库——支付流水、账单、订单、积分变动。写入是主线正确性、低延迟与高可用是生死线。OLAPOnline Analytical Processing关注对历史数据的批量分析、报表与挖掘属于读重负载。TigerBeetle 在 docs/concepts/README.md 中被明确定义为业务交易的系统记录system of record for business transactions。TigerBeetle 官方概念文档将 PostgreSQL、MySQL、SQLite 这类通用数据库称为OLGPOnline General Purpose联机通用处理数据库。OLGP 的强项是灵活查询与通用数据建模但当它们被用来承载大规模 OLTP 时就会暴露出结构性短板。世界正变得越来越事务化OLTP 负载的增长引擎文档指出过去十年间 OLTP 工作负载增长了 34 个数量级。推动这一增长的是交易粒度从月度/日级被压缩到实时/秒级的结构性变化实时支付网络印度 UPI统一支付接口实时支付网络在 2019 年全年处理了 100 亿笔支付而仅 2025 年 1 月单月其处理量就达到 169 亿笔——单月超过十年前的全年纪录。清洁能源与智能电表能源开始按千瓦时kWh进行交易用户账单从月末一次性结算变为每 15 或 30 分钟结算一次。Serverless API 计费按秒或按请求收费而非按月收费。文档特别指出当前大规模 serverless 计费常借助 MapReduce 批处理实现这使得向客户提供实时消费上限变得困难甚至不可能——你无法在一个明天才算出账单的系统里回答你现在还剩多少额度。当交易粒度变细、频率变高、延迟要求变严时通用 OLGP 数据库已经很难跟上文档原话OLGP databases already struggle to keep up。而 TigerBeetle 的定位正是为今天以及未来数十年的 OLTP 规模而构建。它并不取代 OLGP 数据库而是与它们协作——OLGP 继续存放不常更新的业务数据TigerBeetle 则承担高频、高价值的交易流水处理为整个系统提供极致的延迟与吞吐。写密集型工作负载从底层就为写优化OLTP 与 OLGP 最显著的分野在于读写比例。文档指出OLTP 聚焦于记录——业务交易绝大多数是写入操作OLGP 数据库通常按读重或读写均衡负载设计——索引、缓存、查询优化器的大量投入都是围绕读展开的。TigerBeetle 则从零开始为写密集型负载优化optimized from the ground up for write-heavy workloads。这一点在源码层面可以得到印证TigerBeetle 的状态机以**批量batch**为基本处理单元而非单条记录。在 src/state_machine.zig 中StateMachine.batch_max结构明确定义了每次操作可携带的最大事件数上限它由操作类型Operation与单条消息体大小constants.message_body_size_max共同推导得出并通过编译期comptime断言保证create_accounts、create_transfers、lookup_accounts、lookup_transfers均大于 0pub const batch_max struct { pub const create_accounts: u32 max( Operation.create_accounts.event_max(constants.message_body_size_max), // ... 兼容历史操作类型 ); pub const create_transfers: u32 max( Operation.create_transfers.event_max(constants.message_body_size_max), // ... ); // lookup_accounts / lookup_transfers 同理 };配套的模糊测试 src/vsr/multi_batch_fuzz.zig 使用stdx.BoundedArrayType(u32, 8190)验证多批次场景对应性能文档中单次查询最多 8190 笔转账的批量上限详见 docs/concepts/performance.md。这种按批处理、复制成本按批摊销的设计是 TigerBeetle 在保持强一致复制的同时仍能逼近内存哈希表速度的关键。热账户高争用无行锁的强一致性业务交易天然涉及多个账户一笔收入到账随之而来的是手续费、税费、分成、成本分摊等一连串记账。这导致 OLTP 系统中总存在参与极高比例交易的热账户——比如企业的主营收入账户、资金归集账户。在传统数据库里为了保证对热账户的并发更新一致通常使用行锁。文档直言由此产生的争用contention可以让整个系统的性能降到爬行bring the systems performance to a crawl。TigerBeetle 的解法是釜底抽薪提供强一致性保证但不使用行锁——从而绕开热账户上的锁争用由于 TigerBeetle 利用系统缓存system cache的顺序访问特性处理这类交易的吞吐甚至会随负载提升而增加文档原话transactions processing speed evenincreases。这与传统数据库负载越高、锁竞争越严重、性能越差的曲线截然相反是专为 OLTP 高争用场景设计的核心收益之一。业务交易不擅长分片为什么横向扩展不适合 OLTP面对负载增长业界最常见的扩容手段是横向扩展水平分片让不同服务器处理不同集合的交易。但文档明确指出业务交易恰恰是最不擅长分片的负载类型大多数账户无法被干净地切分到各个分片——一个客户的多笔资金往来天然跨越多个业务维度跨分片的交易账户 A 在分片 1、账户 B 在分片 2会变得更复杂、更慢需要分布式事务协调热账户的行锁问题在跨分片场景下会进一步恶化——无论怎么分片高热度账户仍会集中在某几个分片上成为新的瓶颈。另一种思路是用 MapReduce 做计费但文档指出这会让实时余额查询和实时消费上限变得难以实现并造成系统设计完成后极难修复的糟糕用户体验。TigerBeetle 的选择是不分片采用单核设计single-threaded by design加独特的性能优化来获得高吞吐从而同时避开横向扩展的所有副作用。这一设计取舍的完整论证在 docs/concepts/performance.md 的Single Threaded By Design一节中有详细展开——在金融数据库里分片极为困难少量热账户占据大量交易负责这些账户的分片必然成为瓶颈因此用单领导者节点顺序处理事件、通过增加副本节点提升可靠性而非吞吐反而是一种更优的工程决策。单核设计背后的工程支撑结合性能文档TigerBeetle 之所以敢用单核设计去冲击高吞吐是因为每一层都做了针对性工程全部逻辑内置于数据库业务逻辑以 Debit/Credit借贷记账语言直接与数据库对话不需要把业务翻译成多条 SQL 再往返应用与数据库之间避免了跨网络持锁这一昂贵操作全栈自研、零依赖所有层协同为 OLTP 设计无第三方黑盒语言选型使用无垃圾回收、面向高性能系统编程的 Zig 语言编写数据结构手工打磨转账对象仅 128 字节、按缓存行对齐执行一批转账就是一个紧凑的 CPU 循环。这一设计在 src/tigerbeetle.zig 中得到直接印证——Account结构体通过编译期断言强制sizeOf(Account) 128、alignOf(Account) 16128 字节、16 字节对齐且debits_pending/debits_posted/credits_pending/credits_posted四个余额字段一字排开静态内存分配不会内存耗尽、不会因 GC 停顿或互斥锁争用而卡顿、不会产生内存碎片io_uring 原生适配Linux 上零系统调用的网络与存储 I/O。源码中 src/io/linux.zig 直接调用linux.io_uring_cqe/io_uring_sqe等内核接口并处理io_uring不可用时的降级错误提示src/io/darwin.zig 也注释说明其行为与 Linux io_uring 保持一致。系统的瓶颈数据库决定你的业务上限文档用一个直白的论断点明 OLTP 数据库的战略地位你能做多少业务取决于数据库能撑住多少业务。You can only do as much business as your database supports.这意味着需要一个核心 OLTP 数据库能扛住你最忙时段的全部交易并且这个容量要能支撑未来数十年的增长如果系统只是刚好压着吞吐目标运行任何一次意外延迟或运维事故都会导致交易丢失而如果系统只运行在十分之一容量上就为意外留下了充足裕量。TigerBeetle 的设计目标是处理每秒 100 万笔交易1 million transactions per second以消除业务增长超过数据库能力的风险。这并非营销话术而是与上述一系列设计批量处理、无锁一致性、单核流水线、静态内存、io_uring共同构成的工程目标。在 TigerBeetle 中落地 OLTP从概念到 schema理解了 OLTP 的领域特征后下一步自然是用什么数据模型承载这些交易。TigerBeetle 的答案是Debit/Credit借贷记账 / 复式记账——详见 docs/concepts/debit-credit.md。文档在结尾抛出承上启下的问题这个世界越来越事务化OLTP 负载持续增长我们需要一个从头设计来承载它们的数据库。那么这个数据库的完美 schema 和语言是什么答案是 Debit/Credit。在 TigerBeetle 中OLTP 交易的六要素被直接内建进数据模型Who谁在交易debit_account_id与credit_account_id字段What什么资产在流动ledger字段标识资产类型When何时timestamp集群处理时间user_data_64真实世界时间Where何地user_data_32存储交易发生地Why为何code字段映射业务事件枚举How Much多少amount字段。字段级说明可参见 docs/reference/transfer.md更深入的记账原理参见 docs/coding/financial-accounting.md。这种把 schema 内建、把会计不变量如余额上限下沉到数据库内部强制执行的方案正是对OLTP 强调写入、强一致性、热账户无锁这三大领域特征的直接回应。小结OLTP 的本质是实时记录业务交易负载在过去十年增长 34 个数量级而 OLGP 通用数据库已难以支撑OLTP 的三大难题写密集型负载、热账户行锁争用、业务交易难以分片TigerBeetle 的解法写优化的批量状态机单批最高 8190 笔转账见 src/state_machine.zig、无行锁的强一致性、单核顺序处理 io_uring 零系统调用见 src/io/linux.zig并以每秒 100 万笔交易为设计目标协作而非替代TigerBeetle 与承载低频更新数据的 OLGP 数据库协同工作让整个系统获得极致延迟与吞吐下一步理解为什么借贷记账Debit/Credit是 OLTP 的完美 schema请继续阅读 docs/concepts/debit-credit.md若想深入了解单核设计背后的性能哲学请阅读 docs/concepts/performance.md。【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考