
TigerBeetle 的 Debit/Credit 数据模型面向 OLTP 的复式记账模式【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle本文系统讲解 TigerBeetle 为何以Debit/Credit借/贷即复式记账法作为其核心数据模型以及这一沿用数百年的记账范式如何与 OLTP在线事务处理工作负载的需求天然契合。你将理解 OLTP 的六何信息如何一一映射到Account与Transfer两个实体上掌握debit_account_id、credit_account_id、ledger、code、amount、user_data_*等字段的语义与用法并了解 TigerBeetle 如何在数据库内部强制记账不变量与不可变性从而避免应用层手写会计逻辑。读完本文你将具备基于 TigerBeetle 设计账务系统的数据建模能力。OLTP 的六何谁、什么、何时、何地、为何、多少OLTP 的核心是实时记录业务交易——无论是支付、销售、共享出行订单、游戏得分还是 API 用量。无论业务形态如何OLTP 与业务交易记录的信息类型高度一致可以归纳为六何Who谁哪些账户参与了交易What什么正在流动的是哪类资产或价值When何时交易何时发起、何时最终确认Where何地交易发生在世界上的哪个地方Why为何这是哪种类型的交易为什么发生How Much多少转移的资产或物品的数量是多少任何一个需要被完整记录的商业事件几乎都可以拆解为这六个维度的信息。这正是 Debit/Credit 模式得以一种模式包打天下的基础。商业界的通用语言复式记账法Debit/Credit复式记账法是商业与会计领域通行了数个世纪的标准语言其历史至少可以追溯到 13 世纪。支撑复式记账体系的核心洞见是每一笔转账都记录着价值从一个或多个账户流向另一个或多个账户。钱永远不会凭空出现也永远不会凭空消失。这一简单原则保证了企业所有的资金都能被完整核算。Debit/Credit 恰好完整捕获了 OLTP 的六何同时保证了财务一致性。它最小且完备只需两个实体账户 accounts、转账 transfers和一个不变量每一笔借记都有等额且方向相反的贷记就能建模任何领域中的任何价值交换。对于不熟悉借贷记账细节的开发者仓库中的 金融会计入门 提供了更深入的讲解包括五大账户类型资产、负债、权益、收入、费用、借贷如何增减各类型账户余额的规则表以及会计是一种类型系统的直觉建立方法。为什么 SQL 不适合写入密集型 OLTPSQL 是极佳的查询语言擅长把数据从数据库里取出来但 OLTP 的主要工作是把数据放进去——而 SQL 恰恰在这一点上力不从心一个业务交易往往需要多条 SQL 查询每个交易大约需要 10 条 SQL 查询级别这些查询还可能涉及应用与数据库之间的多轮往返round-trip每一轮往返都增加了延迟也增加了在应用层手工维护一致性逻辑的复杂度。TigerBeetle 的思路是既然 OLTP 的写入模式如此固定那就为 OLTP 的 schema 和需求专门设计数据库把会计逻辑在数据库内部强制执行同时大幅提升性能。这也是概念文档中强调的它从外表到内里看起来都不像一个典型的 SQL 数据库的根本原因。TigerBeetle 在数据库内部强制执行 Debit/CreditOLTP 的 schema 被直接内建于 TigerBeetle 的数据模型中开箱即用。六何与 Transfer 字段的对应关系如下维度字段说明Whodebit_account_id、credit_account_id标识参与转账的借方与贷方账户Whatledger每类资产或价值在 TigerBeetle 中由独立的 ledger账本跟踪该字段指示转移的是什么Whentimestamp每个转账拥有唯一的、由集群处理时分配的时间戳若需表示真实世界中的业务时间可存入user_data_64Whereuser_data_32用于存储转账发生地如时区、司法辖区Whycode存储转账发生的原因应映射为应用中所有可能业务事件的枚举或表How Muchamount转移的资产或物品数量对各字段的类型与约束TigerBeetle 有严格规定详见 Transfer 参考amount是 128 位无符号整数code是 16 位无符号整数且不可为零ledger是 32 位无符号整数且不可为零debit_account_id与credit_account_id必须指向已存在的账户且二者不能相同账户不能向自己转账。用user_data_*补充六何的细节从源码结构看src/tigerbeetle.zig 中的Transfer结构体user_data_128、user_data_64、user_data_32是三个任意含义的辅助标识字段数据建模文档 建议的典型用法是user_data_128存储谁和/或什么例如指向控制平面数据库中业务实体的指针user_data_64存储真实世界的第二个时间戳模拟双时态 bitemporality或当 128 位字段不需要时替代其用途user_data_32存储何地如交易发起的司法辖区——跨境汇款场景中仅有 UTC 时间戳可能不够还需要知道交易发生的时区。开箱即用的高级原语除了单笔转账TigerBeetle 还支持两阶段转账先通过flags.pending预留资金进入debits_pending/credits_pending再通过flags.post_pending_transfer过账或flags.void_pending_transfer撤销也支持基于timeout的自动过期链接事件通过flags.linked将多笔转账组成原子链要么全部成功、要么全部失败基于以上原语形成了丰富的数据建模模式与配方覆盖货币兑换、多借多贷、账户关停、余额边界、纠错转账、限流等常见场景。关键点在于余额上限等会计不变量在数据库内部强制执行避免了数据库与应用逻辑之间的往返。从 create_transfers 结果码 可以看到当借方账户设置了flags.debits_must_not_exceed_credits而转账会突破debits_pending debits_posted amount credits_posted时转账会被直接拒绝exceeds_credits——这些校验全部发生在集群内部。不可变性是必需的Debit/Credit 系统的另一关键要素是不可变性转账一旦记录就不能删除没有 DELETE转账不能被修改没有 UPDATE撤销通过独立的纠错转账correcting transfers实现从而保留一份完整、可审计的业务事件日志。即使是最强的持久性也无法防止逻辑层面的数据丢失。SQL 允许破坏性的UPDATE与DELETE而 TigerBeetle 强制只追加的不可变性——确保对账与审计畅通无阻。TigerBeetle 中的转账天生不可变不存在某条畸形查询误删数据的可能性。账户同样不可变从 Account 参考 可知账户字段在创建后不能被用户修改余额字段除外由转账驱动更新也不能被删除从而为审计追踪提供强保证。Transfer文档中也明确了相应保证成功创建的转账永不修改、同一id至多存在一笔转账、待处理转账至多结算一次。误删行或表在任何数据库中都很糟糕但在会计场景下不可接受法律合规与良好商业实践要求所有资金被完全核算、所有历史被完整保留。在不可变中保护用户隐私许多应用必须遵守 GDPR 等隐私保护要求如被遗忘权。用户隐私可以通过有意识地选择存入user_data_*字段的数据来保护例如若用user_data_128保存应用自有user_id到 TigerBeetle 的映射则遗忘该用户只需删除这份映射没有这份映射TigerBeetle 中的账户与转账就无法回溯到真实用户从而在保留审计记录的同时使数据对第三方不可读保护用户隐私。不要自己造账本许多公司最初都会自建记录业务交易的系统等到业务规模化后才意识到需要一个真正的账本最终回到借贷记账法。文档中列举了三个知名案例均为公开报道的事实非本项目数据Uber2018 年起Uber 投入 2 年、40 名工程师将其收款与付款平台迁移到基于复式记账与借贷原理的体系Airbnb2012 至 2016 年间Airbnb 曾用基于 MySQL 的数据管道记录所有交易到不可变存储中用于报表但管道过于复杂、难以扩展且缓慢最终重建了基于复式记账的财务报告系统Stripe依赖一个基于复式记账与不可变事件日志的内部系统记录其处理的所有支付。这些案例说明了一个共同结论借贷记账不是可选项而是规模化商业系统绕不开的终点。标准化、简单、可扩展从某个角度看Debit/Credit 似乎是一个受限的数据模型但它极其灵活且可扩展任何业务事件都可以被记录为借与贷——事实上会计师们已经这样做了数百年。相比把业务交易建模为一堆临时拼凑的表与关系借贷记账提供了一套简单、标准化的 schema可用于现在与未来的所有产品线添加新功能时无需新增列、表以及它们之间的复杂关系避免复杂的 schema 迁移无需为不同业务域分别设计表结构。Debit/Credit 是通用的 schema是数百年来商业的基石。你可以直接利用 TigerBeetle 为 21 世纪 OLTP 而构建的高性能实现。源码层面的印证数据模型如何落地从实现层面可以进一步印证上述设计相关源码见 src/tigerbeetle.zigAccountsrc/tigerbeetle.zig#L10 附近是一个 128 字节的extern struct包含debits_pending、debits_posted、credits_pending、credits_posted四个余额累计字段以及user_data_128/64/32、reserved、ledger、code、flags、timestampTransfersrc/tigerbeetle.zig#L85 附近以debit_account_id与credit_account_id两个 128 位字段建模一笔借方 一笔贷方amount记录金额账户层面还有便捷方法debits_exceed_credits与credits_exceed_debitssrc/tigerbeetle.zig#L34-L43直接对应余额不变量的校验逻辑create_transfer的实现位于 src/state_machine.zig其中对借/贷账户的debits_posted/credits_posted进行更新并校验所有不变量——这正是会计逻辑内建于数据库的代码级证据。此外Account 参考文档 明确给出了全局守恒不变量所有账户debits_pending之和等于credits_pending之和所有账户debits_posted之和等于credits_posted之和——这正是钱从不凭空出现在数据层面的直接体现。小结与下一步OLTP 的六何信息可以一一映射到 TigerBeetle 的Account/Transfer字段复式记账以两个实体 一个不变量建模任何价值交换并在数据库内部强制执行避免应用层往返不可变性无 UPDATE/DELETE、纠错用独立转账保障审计与合规同时可通过审慎使用user_data_*兼顾隐私Debit/Credit 是通用、标准化、可扩展的 schema无需为新产品线反复设计表结构与迁移。接下来可以继续阅读 性能 章节了解专为 OLTP 设计的数据库如何同时获得卓越的性能与安全性另见 安全以及深入 数据建模 与 各类实操配方 以落地你的账务系统设计。【免费下载链接】tigerbeetleThe financial transactions database designed for mission critical safety and performance.项目地址: https://gitcode.com/GitHub_Trending/ti/tigerbeetle创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考