惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

IT之家
IT之家
C
CXSECURITY Database RSS Feed - CXSecurity.com
V
Visual Studio Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
小众软件
小众软件
L
LangChain Blog
Cyberwarzone
Cyberwarzone
美团技术团队
The Register - Security
The Register - Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tor Project blog
V
V2EX
Security Archives - TechRepublic
Security Archives - TechRepublic
Hacker News: Ask HN
Hacker News: Ask HN
L
LINUX DO - 最新话题
Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
aimingoo的专栏
aimingoo的专栏
人人都是产品经理
人人都是产品经理
F
Full Disclosure
V2EX - 技术
V2EX - 技术
The Cloudflare Blog
博客园 - 叶小钗
T
Threat Research - Cisco Blogs
阮一峰的网络日志
阮一峰的网络日志
G
GRAHAM CLULEY
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Latest news
Latest news
S
Security @ Cisco Blogs
Spread Privacy
Spread Privacy
Project Zero
Project Zero
K
Kaspersky official blog
MyScale Blog
MyScale Blog
Attack and Defense Labs
Attack and Defense Labs
云风的 BLOG
云风的 BLOG
博客园 - 【当耐特】
Hacker News - Newest:
Hacker News - Newest: "LLM"
大猫的无限游戏
大猫的无限游戏
P
Privacy International News Feed
Google DeepMind News
Google DeepMind News
WordPress大学
WordPress大学
C
Cybersecurity and Infrastructure Security Agency CISA
Webroot Blog
Webroot Blog
罗磊的独立博客
Vercel News
Vercel News
N
News and Events Feed by Topic
A
Arctic Wolf
C
CERT Recently Published Vulnerability Notes

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

国密算法与国密 TLS 系列索引 【系统架构设计】架构质量属性:不只是"高可用高性能" 【系统架构设计百科】告警策略:如何避免"狼来了" 【系统架构设计】CQRS:读写分离的架构哲学 【系统架构设计】空间架构:极端扩展场景的解法 【系统架构设计】微服务架构深度审视:优势、代价与适用边界 【系统架构设计】扩展性原理:水平、垂直与对角扩展 【系统架构设计】无状态设计:扩展的第一步也是最难的一步 【系统架构设计】缓存架构:从本地到分布式的多级缓存体系 【系统架构设计】管道与过滤器:Unix 哲学的架构表达 【系统架构设计】复杂性管理:架构的核心战场 【系统架构设计】消息队列架构:异步解耦的设计与陷阱 【系统架构设计】CDN 架构:全球加速的设计原理 【系统架构设计】连接池设计:被忽视的性能杀手 【系统架构设计】弹性设计模式:熔断器、舱壁与超时 【系统架构设计】高可用设计模式:冗余、故障转移与仲裁 【系统架构设计】容量规划:从拍脑袋到数据驱动 【系统架构设计】数据库扩展:分库分表的工程实践与替代方案 【系统架构设计】SLO 工程:可靠性的量化管理 【系统架构设计】性能建模:用数学思维分析系统瓶颈 【系统架构设计】混沌工程:主动验证系统的韧性 【系统架构设计】零拷贝与内存映射:数据搬运的极致优化 【系统架构设计】线程模型:从 thread-per-request 到协程 【系统架构设计】容灾架构:多活与灾备设计 【系统架构设计】数据库性能模式:索引、查询与连接管理 【系统架构设计】数据建模:从关系范式到文档模型的真实权衡 【系统架构设计】吞吐量优化:批处理、流水线与并发模型 【系统架构设计】流处理架构:从批处理到实时的范式迁移 【系统架构设计】搜索引擎架构:倒排索引之上的系统设计 【系统架构设计】时序数据架构:监控与 IoT 的存储设计 【系统架构设计】数据迁移与版本化:在线不停机的数据演进 【系统架构设计】数据湖与数据仓库:分析架构的演进路线 【系统架构设计】API 网关设计:入口层的职责边界 【系统架构设计】应用层数据一致性模式:在正确性与性能之间走钢丝 【系统架构设计】多模数据库选型:Polyglot Persistence 的工程实践 【系统架构设计】服务发现与注册:动态拓扑的基础设施 【系统架构设计】配置管理架构:从配置文件到配置中心 【系统架构设计】全链路压测:大规模系统的性能验证 【系统架构设计】幂等性设计:分布式环境下的安全重试 【系统架构设计】契约测试与 Schema 演进:服务间的信任协议 【系统架构设计】长连接与推送架构:WebSocket、SSE 与 MQTT 【系统架构设计】延迟分析:从 P50 到 P999 的全链路追踪 【系统架构设计百科】DDD 战术模式:聚合、实体与值对象 【系统架构设计百科】防腐层与开放主机服务:系统集成的 DDD 方案 【系统架构设计百科】领域事件与事件风暴:从业务到架构的桥梁 【系统架构设计百科】CQRS + Event Sourcing 完整实战:从领域建模到部署 【系统架构设计百科】DDD 与微服务:用领域模型划分服务边界 【系统架构设计】DDD 战略设计:限界上下文与上下文映射 【系统架构设计百科】认证架构:从 Session 到 JWT 到 OIDC 【系统架构设计】API 设计哲学:REST vs GraphQL vs gRPC 的真实权衡 排序算法专题:从 TimSort 到并行排序 【密码学百科】国密算法体系:SM2/SM3/SM4/SM9 全景解读 【密码学百科】承诺方案:Pedersen 承诺、向量承诺与多项式承诺 【密码学百科】不经意传输与隐私信息检索:OT、OT 扩展与 PIR 【密码学百科】门限密码学:门限签名、门限解密与分布式密钥生成 完美哈希:从理论到 gperf 实践 【密码学百科】安全多方计算:从 Yao 的混淆电路到实用 MPC 【密码学百科】同态加密:从 Paillier 到全同态加密(FHE) 【密码学百科】零知识证明系统:zk-SNARKs、zk-STARKs 与 Bulletproofs 【密码学百科】概率论与密码分析:生日攻击、差分分析与线性分析 【密码学百科】计算复杂性与归约:密码安全性证明的基石 【密码学百科】秘密共享:Shamir 方案、VSS 与安全多方计算入口 【密码学百科】椭圆曲线代数:Weierstrass 方程、点群运算与曲线选择 【密码学百科】离散对数与配对密码学:从 DLP 到 BLS 签名 【密码学百科】格密码数学基础:SVP、LWE 与格基约化 【密码学百科】抽象代数:群、环、域的密码学视角 【密码学百科】有限域算术:GF(2^n) 运算与在 AES/ECC 中的应用 【密码学百科】数论进阶:二次剩余、椭圆曲线上的 Weil 配对 【密码学百科】密码学简史:从凯撒密码到量子时代 【密码学百科】威胁模型与安全目标:CIA 三要素之外 【密码学百科】Kerckhoffs 原则与现代密码设计哲学 【密码学百科】随机性:密码学的基石 【密码学百科】信息论入门:熵、完美保密与 Shannon 定理 【密码学百科】分组密码原理:Feistel 网络与 SPN 结构 【密码学百科】AES 逐步拆解:SubBytes 到 MixColumns 的数学 【密码学百科】分组密码工作模式全览:ECB/CBC/CTR/OFB/CFB 【密码学百科】流密码:RC4 的兴衰与 ChaCha20 的崛起 【密码学百科】密码学哈希函数:MD5→SHA-2→SHA-3 的进化之路 【密码学百科】MAC 与 HMAC:消息认证的正确姿势 【密码学百科】认证加密(AEAD):GCM、ChaCha20-Poly1305 与 OCB 【密码学百科】密钥派生函数:HKDF、PBKDF2、Argon2 与密码存储 【密码学百科】公钥密码的数论基础:模运算、群、原根 【密码学百科】RSA 从原理到攻击:教科书 RSA 为什么不安全 【密码学百科】Diffie-Hellman 密钥交换与离散对数问题 【密码学百科】椭圆曲线密码学(ECC):从几何直觉到点群运算 【密码学百科】数字签名:ECDSA、EdDSA 与 Schnorr 签名 【密码学百科】现代密钥交换:X25519、ECDHE 与前向保密 【密码学百科】混合加密与 KEM/DEM 范式:ECIES 与 HPKE 【密码学百科】填充方案:PKCS#1 v1.5、OAEP 与 PSS 【密码学百科】TLS 协议全解析:从握手到 0-RTT 【密码学百科】PKI 与数字证书:信任链的构建与崩塌 【密码学百科】密码认证协议:从 SRP 到 OPAQUE 【密码学百科】零知识证明入门:如何证明你知道而不泄露 【密码学百科】安全信道构造:Noise 协议框架与 Signal 协议 【密码学百科】密钥管理工程:HSM、KMS 与密钥生命周期 【密码学百科】侧信道攻击:从时序攻击到功耗分析 【密码学百科】密码学实现陷阱:三层漏洞分类、审计工具链与系统性预防 密码敏捷性:如何设计可升级的密码系统 【密码学百科】OpenSSL/BoringSSL 架构剖析:ENGINE、Provider 与 FIPS 模块 排序基准测试:用数据说话
【金融科技工程】账务数据库设计:TiDB/OceanBase/Postgres 下的分片、索引、热点账户
2026-04-22 · via 土法炼钢兴趣小组的算法知识备份

引子

上一篇《复式记账工程化》把账务模型落到了 accountsjournal_entriespostings 三张表。到这里,设计图纸画得再漂亮,也挡不住一个现实问题:这三张表放哪儿

“放数据库”三个字在账务场景里远不是一行 CREATE TABLE 能解决的。一个成熟支付机构,日分录(postings)量级在亿到十亿级,单行写入必须带事务,读写延迟在 P99 十毫秒以内,还要满足 RPO=0(零数据丢失)、RTO<1min(故障一分钟切换)、审计数据保留七到十年、任何一行都不能被 UPDATE。你要扛住这些约束,数据库选型、分片策略、索引、归档、并发控制几乎都要重新推一遍。

这篇文章的读者画像是:

  • 支付/交易/银行核心系统的架构师与 DBA;
  • 正在把单库 MySQL/PostgreSQL 的账务库往分布式迁的工程团队;
  • 想了解 OceanBase、TiDB、TigerBeetle 这些”金融级数据库”实际差异的技术决策者。

我们不会挨个讲 SQL 语法,重点在为什么选、怎么切、怎么扛热点、怎么归档、怎么对审计交差。文末会落到一份选型清单和一张可以直接抄的部署架构图。


一、金融账务的 SLA 框架

账务数据库的设计上限由业务 SLA(Service Level Agreement)决定。离开 SLA 谈”用什么数据库最好”是空中楼阁。

1.1 RPO 与 RTO

  • RPO(Recovery Point Objective):故障发生时最多允许丢多少数据。账务场景基本要求 RPO=0,即任何一笔已确认的分录都不能丢。达成 RPO=0 的前提是同步复制(synchronous replication)或共识协议(Paxos/Raft)多数派确认。
  • RTO(Recovery Objective):从故障发生到业务恢复的时间。支付、卡组织侧通常要求 RTO < 1 min,交易所撮合侧更严格(秒级)。

这两个指标直接排除了”异步主从 MySQL”、“跨可用区异步同步的 PostgreSQL 主从”这类方案在关键账务路径上的独立部署可能。要么叠加同步复制,要么直接上共识协议。

1.2 一致性与隔离级别

账务强一致来源于复式记账的约束:借贷必须同事务成功或同事务失败,余额视图必须反映最新分录。工程语境下至少需要:

  • 事务原子性:一次 journal entry 的所有 postings 要么都写入,要么都回滚;
  • 线性一致读:余额查询必须读到最新提交;这在分布式场景下不是默认行为,MySQL 主从异步复制的从库读会”走到过去”;
  • 隔离级别:至少 READ COMMITTED,对扣款类幂等更新场景需要 REPEATABLE READ 或显式 SELECT FOR UPDATE。Oracle/PostgreSQL 的 SERIALIZABLE 在热点账户上代价太高,一般不做默认级别。

1.3 审计、归档与合规

  • WORM(Write Once Read Many):分录一旦落库就不能改,任何”更正”必须通过红冲分录(reversing entry)体现,数据库层面通过去 UPDATE/DELETE 权限、触发器保护、审计日志三层保障;
  • 归档保留:中国《会计档案管理办法》要求会计凭证、账簿保留 30 年,银行业对明细账和交易流水要求 15 年以上;欧盟 PSD2、美国 SOX 对审计数据保留 7 年;
  • 可追溯:任何一笔分录要能追到它背后的业务事件(订单、交易、风控决策),反向也要能从事件还原账务。

把以上组合起来,账务数据库实际要满足的”组合 SLA”是:

维度 账务数据库目标
写入吞吐 单集群 10k–100k TPS(分录行)
读延迟 P99 < 10 ms
可用性 99.99%(年停机 < 52 分钟)
RPO 0
RTO < 1 min
保留年限 热数据 1–2 年、温 3–7 年、冷 10–30 年
一致性 线性一致、事务原子
审计 append-only、不可篡改

1.4 不同业务形态的差异

不是所有账务都长一个样:

  • 支付账务:高并发写、强事务、热点账户突出(商户、渠道、手续费池);
  • 证券账务:中频写但要求与行情、撮合、清算在同一个”账户日”内严格对齐;
  • 银行核心:写频低于支付侧但单笔金额大,容错要求极严;
  • 交易所内账:极高并发、可接受短时异步持久化(但事务必须最终落地),撮合内存账 + 持久化分离。

后面选型要对照不同形态来看。


二、主流选型对比

把能选的都摆出来,再按成本、规模、案例筛。

2.1 PostgreSQL

优势

  • 类型系统完备:numeric 精确十进制、uuidjsonbrange 类型,对金额建模友好(见《钱的建模》一篇对 numeric(38, 18) 的讨论);
  • 约束强:CHECKEXCLUDE、domain、generated columns 足以在 DDL 层编码余额守恒、币种一致等规则;
  • MVCC 成熟,SERIALIZABLE 实现为 SSI(Serializable Snapshot Isolation),正确性高;
  • 分区(partitioning)、逻辑复制(logical replication)、外部表(FDW)都是一等公民;
  • 生态:TimescaleDB、Citus、CloudNativePG、PostGIS 等扩展丰富。

劣势

  • 写扩展性受限于单机:主库写 TPS 通常在 10k–30k 量级;再往上要分库(Citus / 自研 shard router);
  • 高可用靠外挂(Patroni、repmgr、CloudNativePG + etcd),切换逻辑需要自己保障;
  • 缺乏原生的多主同步写,跨机房强一致需要外部共识。

适用规模:中小规模账务(日分录 < 1 亿)、B 端 SaaS、证券/基金/信托核心。典型用法是主 + 同步复制从 + 异地异步备 + 分区归档。

案例:Stripe 早期账务层核心表建在 PostgreSQL 上;Revolut 2020 年代初期全栈 PostgreSQL;国内不少持牌支付机构的备付金账务使用 PostgreSQL + Patroni 集群。

2.2 MySQL InnoDB

优势

  • 运维生态最成熟,DBA 人才池最大;
  • InnoDB 的行锁 + 聚簇索引对点查和小范围扫描友好;
  • 分库分表中间件成熟(ShardingSphere、MyCat、DRDS、Vitess);
  • 半同步复制(semi-sync)在多数派 ACK 后返回,可以近似 RPO=0。

劣势

  • 数值类型历史包袱(FLOAT/DOUBLE 的误用)导致金额建模需要严格规范;
  • 事务嵌套、在线 DDL 能力逐步改善但仍弱于 PostgreSQL;
  • 单机写扩展到瓶颈后,分库分表是必经之路,而分库分表的跨分片事务需要额外基础设施(XA、TCC、SAGA)。

适用规模:互联网支付、电商账务中等到大规模。阿里/蚂蚁、京东、美团、拼多多、字节跳动的账务体系都有长期在 MySQL 上深耕的历史。

典型演进路径:单机 MySQL → 一主多从 → 垂直拆分(按业务域)→ 水平分库分表(按 account_id / user_id hash)→ 分布式数据库(OceanBase / TiDB)。

2.3 OceanBase

蚂蚁集团自研的分布式关系数据库,原生 Paxos 三副本、MySQL 协议兼容(也兼容部分 Oracle 语法),典型”金融核心场景”产品。

优势

  • Paxos 多数派同步:三副本部署天然 RPO=0;
  • 多租户:同一集群内不同租户资源隔离,账务租户、报表租户、风控租户各自为政;
  • 分布式事务:支持跨分区(partition)两阶段提交,对账务的跨账户事务友好;
  • HTAP:OLAP 列存(OceanBase Labs 的 OB 4.x 引入行列混存)对账单汇总、对账报表场景友好;
  • MySQL 协议兼容:迁移改动相对小;
  • 公开案例:蚂蚁集团 2014 年前后开始把交易 / 账务从 MySQL 分库分表迁到 OceanBase,2017 年双十一已承载核心链路;网商银行、中国工商银行核心、西安银行核心、建行部分场景公开案例可查。

劣势

  • 商业产品,部署运维门槛高于开源 MySQL;
  • 社区版(4.x 之后开源)生态仍在建设,第三方工具适配落后于 MySQL/PG;
  • 部分 Oracle 兼容特性仅在企业版。

适用规模:日分录 10 亿 + 的超大规模账务、银行核心、证券账户体系。

2.4 TiDB

PingCAP 开源的分布式数据库,TiKV + TiDB Server + PD 三组件架构,Raft 多副本。

优势

  • 完全开源,社区活跃;
  • MySQL 协议兼容,迁移路径清晰;
  • TiFlash 列存提供 HTAP 能力,账务 + 报表同一份数据;
  • 自动分片(Region):开发者基本不用手工管理分片,PD 自动负载均衡;
  • 开源案例:中国人寿、微众银行部分场景、平安银行、PayPay(日本)、SmartNews、Pinterest(部分场景)。

劣势

  • 强一致来自 Raft 多数派,跨机房部署的延迟会直接进入写入路径;
  • 单行热点场景(超热账户)需要配合业务侧打散;
  • 乐观事务模式下大事务容易冲突回滚,账务场景应使用悲观事务(TiDB 默认自 5.0 起为悲观);
  • 索引类型较 PostgreSQL 少(无 partial index,GIN/GiST 不可用)。

适用规模:日分录数亿到十亿级、有 HTAP 诉求、开源栈偏好。

2.5 CockroachDB

美国 Cockroach Labs 开源产品(自 2022 年起部分核心代码转为 CockroachDB Community License,非完全 OSI 开源),主打全球部署。

优势

  • 原生 SQL + 跨地域多活,Raft + 范围分区(range partition);
  • 兼容 PostgreSQL 协议;
  • 地域亲和(follower reads、region-aware routing):适合”用户所在地读本地”场景。

劣势

  • 单机性能较 MySQL/PG 单机弱;
  • 国内部署案例少,生态支持有限;
  • 许可证变化后部分商业使用需要付费。

适用规模:跨境支付、全球化 SaaS 账务、需要多地数据主权合规的场景。国际案例如 Bose、DoorDash、Form3(开放银行支付平台)。

2.6 Oracle

优势

  • RAC + Data Guard + GoldenGate 的高可用与复制方案在传统银行被验证 20 年;
  • PL/SQL 在存储过程、物化视图、分区、压缩、闪回上几乎是工业最强;
  • ASM、AWR、优化器、诊断工具链极成熟。

劣势

  • 许可证成本高(按核收费 + 选件包);
  • Exadata、Engineered Systems 强绑定硬件;
  • 在互联网支付级 TPS 上需要 RAC 多节点 + 大内存,规模越大性价比越低;
  • 国内”去 O”趋势明显(工、建、招、平安、各地农信社),存量多于增量。

适用规模:传统银行核心、保险、清算机构。典型案例:中国人民银行第二代支付系统(CNAPS2)历史上依赖 Oracle + DB2,逐步向国产数据库迁移中。

2.7 专用账务数据库:TigerBeetle

一个非常有意思的路线:为复式记账专门设计的数据库,用 Zig 写,单表结构(accounts + transfers)、强一致(VSR/Viewstamped Replication)、单机百万 TPS 设计目标。

优势

  • 数据模型与复式记账 1:1,开发者不用自己在通用数据库上搭 postings 表;
  • 内置余额守恒检查、债务 / 债权双向计算;
  • 固定模式让编译器能做极致优化;
  • 有限状态机 + WAL,崩溃恢复行为清晰。

劣势

  • 仅适用账务场景,报表、BI、审计检索还要走另一套存储;
  • 年轻(2020 年后),生产案例仍在积累;
  • 固定 schema 意味着自定义字段要走 user_data 泛化字段,语义要自己约定。

适用规模:新建高性能账务、加密资产交易所内账、游戏内经济系统。

2.8 自研 Ledger:Stripe、Square、大厂内部账务

互联网金融公司普遍在通用数据库之上包一层”Ledger Service”:

  • Stripe Ledger:在 PostgreSQL(早期)与自研分布式存储(后期)之上建 double-entry 服务;对外 API 即”create transaction / query balance”;
  • Square/Block:MySQL 分库分表 + 自研 Subledger 服务;
  • 蚂蚁集团”全域账务(OmniLedger / GTS)“:OceanBase + 分布式事务服务(GTS/DTX),上层业务不直接操作分录表,而是调用账务服务;
  • Uber Ledger:长期 MySQL 分库 + Cassandra 归档,近年向 Docstore / 自研迁移。

这里的核心启发:数据库不是账务的全部,账务服务层负责”复式约束、幂等、热点拆解、审计落库”,数据库负责”存、查、事务、复制”

2.9 一张选型对比表

数据库 开源 协议 副本协议 RPO=0 典型规模(日分录) 代表案例
PostgreSQL PG 逻辑/物理复制 依赖同步复制配置 < 1 亿 Stripe 早期、Revolut
MySQL + 分库分表 MySQL semi-sync 近似 10 亿 + 阿里早期、京东、Square
OceanBase 社区版开源 MySQL/Oracle Paxos 100 亿 + 蚂蚁、网商、工行
TiDB MySQL Raft 10 亿 + 微众、中国人寿、PayPay
CockroachDB BSL PG Raft 1–10 亿 DoorDash、Form3
Oracle Oracle Data Guard 10 亿 + 传统银行核心
TigerBeetle 自定义 VSR 设计百万 TPS 少量新系统

三、分片维度的选择

只要数据库在单机或单集群规模达到瓶颈,就必须分片。分片维度直接决定了事务代价热点分布索引成本运维复杂度

3.1 按用户(user_id / account_owner_id)分片

适用:面向 C 端的钱包、银行核心、证券经纪业务。

优点

  • 同一用户的账户、分录、订单都落在同一分片,单用户操作是本地事务;
  • 绝大多数查询(“我的账单”、“我的余额”)天然命中单片。

缺点

  • 跨用户转账是跨分片事务(A 用户分片 → B 用户分片),需要 2PC / TCC / SAGA;
  • 商户、平台、渠道这类”共享账户”天然不属于任何 C 端用户,需要额外”系统账户分片”;
  • 用户活跃度差异大,热点用户(大 V、头部商户)会造成分片负载不均。

3.2 按科目(account_code)分片

适用:总账(General Ledger)、财务报表系统。

优点

  • 报表查询按科目天然命中单片;
  • 科目数量有限,分片规模可预测。

缺点

  • 单一科目(如”手续费收入”、“用户钱包总额”)数据量巨大,分片不均;
  • 一笔业务的借贷两侧通常涉及不同科目(用户应付 → 手续费收入),写入即跨分片;
  • 不适合交易明细账。

3.3 按时间分片

适用:流水表、事件日志、审计日志。

优点

  • 热数据集中在最近时间段,老数据自然沉到冷分片;
  • 按日/月创建分区,归档、清理、压缩都方便;
  • 配合 PostgreSQL 原生 partitioning 或 TiDB 的分区表表达力强。

缺点

  • 写入集中在”当前分区”,当前分区压力大(典型的”尾写热点”);
  • 跨时间查询需要分区裁剪优化,否则全量扫。

3.4 按机构(institution_id / merchant_id)分片

适用:B 端 SaaS、PaaS 支付平台,一个租户就是一个机构。

优点

  • 多租户隔离清晰;
  • 单租户事务本地化;
  • 计费、配额、限流按分片粒度容易实现。

缺点

  • 大租户 vs 小租户差异可能巨大(头部商户的流水是小商户的千倍);
  • 跨机构业务(清算、分账、代发)天然跨分片。

3.5 组合维度与”分片键即路由”

实战中极少单维度,典型做法:

  • 一级分片键user_id(或 merchant_id),决定路由到哪个逻辑分片;
  • 二级分区:时间(按月),决定在该分片内的存储位置;
  • 全局表:币种、科目表、机构表等小表,在所有分片复制(broadcast table)。

以蚂蚁/支付宝早期的经验为例(公开资料可查):账户表按用户 ID hash 分 N 片(N 从 100 到 上千再到 OceanBase 之后不再可见),每片再按月分区。商户 / 渠道 / 平台账户单独放在”系统分片”里,和普通用户 C2C 路径走不同库。

3.6 跨分片事务的代价

真实数字不夸张地说:跨分片事务是单分片事务成本的 5–10 倍。原因:

  1. 多段 RPC / 两阶段提交协议额外网络;
  2. 分布式锁持有时间变长,并发度下降;
  3. 故障模式增多(协调者故障、参与者故障),需要补偿;
  4. 监控、审计、幂等语义变复杂。

应对策略:

  • 减少跨分片事务数量:能单分片就单分片。用户间转账走”入总账 + 异步反向分录”的 SAGA 模式,而非强一致 2PC(详见下一篇《幂等、事务与一致性》);
  • 批处理合并:夜间对账、清算、结算这类批量跨分片操作走”批量 + 对账补偿”;
  • 分布式事务服务:OceanBase、TiDB、GTS(蚂蚁)原生提供,用好它们就行,不要自己写 2PC。

3.7 分片演进的工程原则

  1. 分片键一旦确定,极难更换。上线前务必压测头部与长尾的数据倾斜;
  2. 分片数量取 2 的幂或质数:2 的幂便于一致性哈希扩容;
  3. 预留分片:上线时分片数 > 当前需要,比如未来 3 年的 2 倍容量;
  4. 路由层独立:ShardingSphere、Vitess 这类在应用和数据库间插一层,而不是把路由逻辑写在应用里;
  5. 分片映射元数据本身要高可用:路由信息放在 etcd / ZK / PD 里,不能丢。

四、热点账户:金融系统的”头号 BUG 制造机”

4.1 什么是热点账户

一笔 C2C 转账可能涉及:

  • 付款人钱包账户(几千万个,分布均匀);
  • 收款人钱包账户(同上);
  • 平台手续费账户(只有一个,每笔都写);
  • 备付金归集账户(每个渠道 1 个,每笔都写);
  • 损益科目、暂存科目等(少数几个,高频写)。

前两者是分布账户,后三者是集中账户 = 热点账户。一台机器哪怕 SSD、哪怕 InnoDB,行级锁争抢也会卡死在那几行上。

公开案例:Uber 2019 年 CNCF 演讲披露早期 MySQL 上 “platform fee account” 单行 QPS 瞬时过万,成为整个支付链路瓶颈;蚂蚁的”贷款平台总账”、支付宝的”手续费总账”都是同类问题的历史体现。

4.2 Shadow Account(影子账户)/ 分桶

思路:把一个逻辑账户拆成 N 个物理桶(subaccount / shadow),写入时随机或哈希打散到某个桶,余额查询时对 N 个桶求和。

-- 原始(热点)
UPDATE accounts SET balance = balance + 100 WHERE account_id = 'FEE-POOL';

-- 分桶后
-- 写入:对 merchant_id hash 到桶
UPDATE accounts_buckets
SET balance = balance + 100
WHERE account_id = 'FEE-POOL' AND bucket = hash(merchant_id) % 64;

-- 读取:聚合
SELECT SUM(balance) FROM accounts_buckets WHERE account_id = 'FEE-POOL';

工程注意

  • 桶数量要预估:过多浪费索引,过少仍然热点;典型 64 / 128 / 256 起步;
  • 聚合查询有延迟代价,准实时余额场景要配合缓存;
  • 对账要改:原本 SELECT balance FROM accounts WHERE account_id=... 改成聚合;
  • 桶的归属要稳定(同一个 merchant 永远路由到同一个桶),否则对账历史接不上;
  • 热点账户不止”写”,读也可能热(审计查询全平台手续费)。

4.3 批量合并(Batching)

思路:业务写入不直接打到热点账户行,先写入到”待合并队列”(一张流水表或 Kafka),定时(秒级)批处理合并一笔大额分录落到热点账户。

业务事件 → 写入 fee_pending 表(按 merchant 一行一笔,不争抢)
            → 每 N 秒或每 M 条 flush:
                 INSERT 一笔合并分录到 journal
                 UPDATE FEE-POOL 账户余额 += SUM(pending)
                 DELETE fee_pending

适用:手续费池、渠道归集、平台总账这类”可容忍秒级延迟”的账户。

代价

  • 余额准实时性下降,报表侧要清楚这一点;
  • 合并窗口内系统故障需要保证 fee_pending 不丢(WAL、持久化 MQ);
  • 合并事务本身变大,要做好分批。

4.4 事件溯源(Event Sourcing)

思路:账户余额不是”现值”,而是”从创建到现在所有事件的累加”。写入就是 append 一条事件(几乎无锁),余额是一个视图(可物化)。

events: (account_id, seq, delta, ts, event_id)
  PK: (account_id, seq)
  写:INSERT, 按 account_id 自然分散
  读余额:SUM(delta) WHERE account_id=...;或物化视图 / snapshot + 增量

优点

  • 无行更新,纯 append,写入吞吐高;
  • 天然审计:每个事件保留原因;
  • 与 Kafka / Pulsar 等事件系统天然契合。

缺点

  • 余额计算需要快照 + 增量,冷账户首次查询慢;
  • 业务侧的”余额校验 / 负余额禁止”变成读后写,需要乐观控制;
  • 热点账户依然是(所有写都集中到 FEE-POOL),需要结合分桶。

TigerBeetle 内部模型近似于这个思路。Stripe Ledger 的 postings 表也有强烈 append-only 色彩。

4.5 异步双写与对账兜底

对超热账户,有些系统走”同步写分布账户 + 异步写热点账户”:

  • 付款人扣款同步;
  • 手续费池写 Kafka,后台消费器消费并合并写入热点账户;
  • 日终对账核对”从分布账户流出的手续费总额” = “热点账户增量”。

这是工程折中,必须配对账。否则”异步丢了几条”无人发现。


五、索引设计

5.1 账务核心索引模式

以上篇的 postings 表为例:

CREATE TABLE postings (
  posting_id       BIGINT PRIMARY KEY,
  entry_id         BIGINT NOT NULL,
  account_id       BIGINT NOT NULL,
  direction        SMALLINT NOT NULL, -- 1 借 / -1 贷
  amount           NUMERIC(38, 4) NOT NULL CHECK (amount > 0),
  currency         CHAR(3) NOT NULL,
  effective_time   TIMESTAMPTZ NOT NULL,
  created_time     TIMESTAMPTZ NOT NULL DEFAULT now(),
  business_type    SMALLINT NOT NULL,
  business_id      BIGINT NOT NULL
);

最关键索引

-- 账户流水查询(最高频)
CREATE INDEX idx_postings_account_time
  ON postings (account_id, effective_time DESC)
  INCLUDE (direction, amount, currency, entry_id, business_id);

-- entry 聚合(对账)
CREATE INDEX idx_postings_entry ON postings (entry_id);

-- 业务回溯
CREATE INDEX idx_postings_business ON postings (business_type, business_id);

几个设计要点:

  1. (account_id, effective_time DESC):账户明细倒序是最频繁的查询模式(“我最近 20 笔”),DESC 索引避免排序;
  2. INCLUDE 覆盖索引(PostgreSQL 11+、SQL Server、MySQL 8.0 generated columns 替代):让热查询在索引内完成,不回表;
  3. entry_id 索引:用于”一笔业务的所有借贷”聚合,对账必用;
  4. 业务回溯索引:风控、客服从业务 ID 反查分录。

5.2 Partial Index / Filtered Index

PostgreSQL、SQL Server 支持 partial index,MySQL 目前只能通过生成列近似。典型场景:

-- 只对未入账的、待处理的分录建索引(占比小)
CREATE INDEX idx_postings_pending ON postings (created_time)
  WHERE status = 'PENDING';

-- 大额分录专用索引(风控监控)
CREATE INDEX idx_postings_large ON postings (effective_time, account_id)
  WHERE amount >= 1000000;

Partial index 的优势:索引变小、更新代价低、大账户的冷数据不污染索引。

5.3 时间分区 + 本地索引

当表达到十亿级,分区变成必须。PostgreSQL 声明式分区:

CREATE TABLE postings (...) PARTITION BY RANGE (effective_time);
CREATE TABLE postings_2026_04 PARTITION OF postings
  FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');
CREATE INDEX ON postings_2026_04 (account_id, effective_time DESC);

要点

  • 每个分区建各自本地索引,分区裁剪能直接跳过旧分区;
  • 分区边界要和业务”会计期间”对齐(月 / 日),以便归档以分区为单位脱离主表;
  • 分区过多(上千)本身是元数据压力,PostgreSQL 13+ 的 partition-wise join / prune 能好很多。

5.4 MySQL / TiDB / OceanBase 的差异

  • MySQL InnoDB:主键即聚簇索引,建议把 (account_id, effective_time, posting_id) 作为主键,排好顺序提升局部性;
  • TiDB:Region 自动切分,主键建议 AUTO_RANDOM 或 composite,避免单调递增写入造成 Region 热点;
  • OceanBase:分区策略(hash/range/list/key)丰富,按 account_id hash 分区能天然打散;
  • PostgreSQL:非聚簇索引,CLUSTER 命令可以按索引物理重排,但在高写入下很少用;用 BRIN 索引可以给时间列加低成本索引。

5.5 索引反模式

  • 宽列无差别索引CREATE INDEX idx ON postings(account_id, entry_id, direction, amount, ...),看上去什么都能走,实际上维护成本高、选择性低;
  • 包装字段索引WHERE DATE(effective_time) = '2026-04-22' 会使索引失效,改为区间 WHERE effective_time >= '...' AND < '...'
  • 大表加索引不在线:PG 要 CONCURRENTLY,MySQL 要 ALGORITHM=INPLACE, LOCK=NONE,TiDB/OB 可以在线;
  • JSON 字段索引:表达式索引或 GIN 可以,但成本要提前压测。

六、冷热分层与归档

6.1 三层数据分层模型

┌─────────────────┐
│  热层(Hot)     │  最近 1–2 年   OLTP 主库(OB/TiDB/PG)
│  全索引、事务    │
├─────────────────┤
│  温层(Warm)    │  2–7 年       列存副本 + 只读副本(TiFlash/OB 列存/PG 归档分区)
│  报表、对账、审计│
├─────────────────┤
│  冷层(Cold)    │  7–30 年      对象存储 + Iceberg + Parquet(S3/OSS)
│  WORM、审计取证  │
└─────────────────┘

6.2 热层:Online

  • 表分区粒度:月 / 周;
  • 索引齐全,支持 P99 < 10ms 查询;
  • 写入、事务、幂等校验都在这一层;
  • 单表数据量控制:建议单分区 1 亿行以内,单表 10 亿以内。

6.3 温层:Near-line

  • 数据迁出 OLTP 主节点,留在温存储(列存副本、OLAP 库);
  • 对账、报表、风控历史回溯走这一层;
  • 典型技术组合:
    • TiDB 主 + TiFlash 列存副本;
    • OceanBase 3.x 的 OLAP 节点 / 4.x 的行列混存;
    • PostgreSQL 主 → 逻辑复制到 Greenplum / ClickHouse / StarRocks / Doris;
    • MySQL → 同步到 Doris / StarRocks / Hudi。
  • 温层查询接口和热层解耦:业务层有”历史查询”专属 API。

6.4 冷层:Archive

  • 目标:长期保留、成本极低、不可篡改
  • 技术栈:S3 / OSS / COS 对象存储 + Apache Iceberg(或 Delta Lake / Hudi)+ Parquet;
  • 写入路径:温层 → ETL → Parquet(按 account_id 分桶、按 effective_time 分区)→ Iceberg 注册;
  • 查询:Trino / Spark / Doris / StarRocks 等引擎直接挂 Iceberg catalog;
  • 合规保证:
    • S3 / OSS 的 Object Lock / Retention 配置 WORM(Compliance 模式),在保留期内连 root 账号都无法删除;
    • 配合 AWS GovCloud、阿里云金融云合规版。

6.5 归档迁移流程

flowchart LR
  A[OLTP 主库] -->|CDC/逻辑复制| B[温层列存]
  A -->|分区 DETACH| C[归档区]
  B -->|ETL 按月| D[Iceberg + Parquet<br/>S3 Object Lock]
  D -->|Trino/Spark| E[审计/取证/报表]
  B -->|只读副本| E

关键点

  1. 归档迁移是按分区粒度做的,不要按行做(代价差 1000 倍);
  2. 迁移完成后,冷层要有校验:行数、SUM(amount)、MAX(effective_time) 三项对齐;
  3. 冷层数据任何变更必须通过”红冲 + 新分录”体现,从不修改历史 Parquet 文件;
  4. 审计团队对冷层的访问走独立权限和只读通道。

6.6 PostgreSQL 归档的实战细节

-- 归档时先 detach 分区
ALTER TABLE postings DETACH PARTITION postings_2020_01 CONCURRENTLY;

-- 导出为 Parquet(COPY + 外部工具 / pg_parquet)
COPY postings_2020_01 TO PROGRAM 'aws s3 cp - s3://archive/postings/year=2020/month=01/data.parquet'
  WITH (FORMAT parquet);

-- 校验无误后 DROP
DROP TABLE postings_2020_01;

实务上建议用工具:

  • pg_partman 自动管理时间分区;
  • pg_parquet / pg_duckdb 直接读写 Parquet;
  • Debezium + Iceberg Sink 做实时 CDC 到冷层。

七、并发控制

7.1 MVCC 基础

PostgreSQL、MySQL InnoDB、Oracle、OceanBase、TiDB 都实现了 MVCC:读不阻塞写、写不阻塞读。账务读查询(余额、明细)几乎都在快照上跑,不持锁。

要点:

  • 快照点选择:事务开始时(REPEATABLE READ)或每个语句开始时(READ COMMITTED);
  • 长事务会拖慢 VACUUM / GC:PostgreSQL 的 pg_stat_activityxact_start 老的会让 dead tuple 无法回收;
  • 快照读的一致性:同一事务内多次读同一余额,不会”闪变”。

7.2 扣款路径:悲观 vs 乐观

悲观(SELECT FOR UPDATE)

BEGIN;
SELECT balance FROM accounts WHERE account_id = ? FOR UPDATE;
-- 业务校验 balance >= amount
UPDATE accounts SET balance = balance - ? WHERE account_id = ?;
INSERT INTO postings (...) VALUES (...);
COMMIT;

优点:语义直观,并发正确;缺点:热点账户锁等待严重。

乐观(版本号 / CAS)

UPDATE accounts
SET balance = balance - ?, version = version + 1
WHERE account_id = ? AND version = ? AND balance >= ?;
-- affected_rows = 0 → 重试或失败

优点:无显式锁,并发高;缺点:冲突率高时全是重试浪费。

选择:分布账户(用户钱包)用悲观简单可靠;热点账户用分桶 + 乐观 CAS。

7.3 死锁避免

跨账户转账经典死锁:事务 A 锁账户 X 再锁 Y,事务 B 锁 Y 再锁 X。

防御

  1. 账户 ID 排序加锁:永远先锁 min(from, to) 再锁 max(from, to)
  2. 统一事务模板:账务服务层做这件事,业务方不接触 FOR UPDATE
  3. 超时短:MySQL innodb_lock_wait_timeout 设 3–5s;
  4. 监控死锁:MySQL SHOW ENGINE INNODB STATUS、PG pg_stat_activity + pg_locks

7.4 隔离级别陷阱

  • READ COMMITTED:同一事务内两次读同一余额可能不同;业务要注意;
  • REPEATABLE READ(MySQL 默认):写冲突以 gap lock 体现,可能阻塞插入;
  • SERIALIZABLE:PG 的 SSI 正确性最高但有回退成本,热点场景不推荐全局用;
  • Snapshot:Oracle 的 SERIALIZABLE 实际是快照隔离,小心 write skew。

7.5 行级锁热点

即便有悲观锁,同一行的高并发更新是逃不掉的。应对就是前面第四节的热点方案——分桶、批量合并、事件溯源

7.6 分布式事务

  • 2PC:OceanBase / TiDB / CRDB 原生支持,延迟代价在 10ms 级;
  • TCC(Try-Confirm-Cancel):业务层补偿,适合跨库跨服务;
  • SAGA:长事务链式补偿,适合跨域流程;
  • XA:MySQL 支持但运维代价高,生产很少用。

下一篇《幂等、事务与一致性》会专门展开。


八、压测与容量规划

真实数字不要乱编,但几个工业常识区间可以参考:

  • 单机 PostgreSQL(SSD、64c、256G):TPC-C 12k–25k TPMC;金融账务负载(小事务、索引命中)10k–30k TPS;
  • 单机 MySQL InnoDB:同配置,TPC-C 15k–30k TPMC;
  • OceanBase:2019 年蚂蚁 TPC-C 官方结果 707 万 tpmC(经过审计);实际金融生产集群单租户十万级 TPS;
  • TiDB:单集群可以扩到数十万 TPS,公开报告参考 PingCAP TiDB Benchmark;
  • CockroachDB:公开 TPC-C 百万 warehouses 级;
  • TigerBeetle:设计目标单实例 1M TPS transfers,实测数据参考其官方 benchmark。

重要TPC-C / TPC-E 是合成负载,和真实账务差异不小。上生产前必须用自己的 业务流量回放 做压测:

  1. 从生产采样最近 7 天流水;
  2. 按账户 ID 重放(保证热点账户比例正确);
  3. 包含读写比(账务侧典型 7:3,一次写配几次流水读);
  4. 跨分片比例不能丢:典型 5%–15% 的事务跨分片;
  5. 灰度切流:10% → 30% → 100%,每步有回退开关。

容量规划的一个经验公式:

峰值 TPS ≈ 日交易量 / 86400 * 峰谷比(3–10)
热点账户 QPS ≈ 峰值 TPS * 热点账户参与率(0.5–1.0)
单库上限 ≈ 单机 TPS * 0.6(保留 40% 冗余)
分片数 ≥ 峰值 TPS / 单库上限

九、数据完整性:让数据库帮你兜底

9.1 Check 约束

ALTER TABLE postings
  ADD CONSTRAINT chk_amount_positive CHECK (amount > 0),
  ADD CONSTRAINT chk_direction CHECK (direction IN (-1, 1)),
  ADD CONSTRAINT chk_currency_iso CHECK (currency ~ '^[A-Z]{3}$');

ALTER TABLE accounts
  ADD CONSTRAINT chk_balance_nonneg
    CHECK (balance >= 0 OR allow_negative = TRUE);
  • MySQL 8.0 起 CHECK 才真正生效;
  • TiDB、OceanBase 支持 CHECK;
  • OceanBase 的”余额不允许为负”场景还可以配合”授信额度”字段。

9.2 余额守恒约束

复式记账的最强约束:每一笔 entry,借方合计 = 贷方合计。可以用触发器或延迟约束:

-- PostgreSQL 延迟约束示例
CREATE OR REPLACE FUNCTION check_entry_balanced(eid BIGINT) RETURNS VOID AS $$
DECLARE net NUMERIC;
BEGIN
  SELECT COALESCE(SUM(direction * amount), 0) INTO net
  FROM postings WHERE entry_id = eid;
  IF net <> 0 THEN
    RAISE EXCEPTION 'Entry % not balanced: %', eid, net;
  END IF;
END;
$$ LANGUAGE plpgsql;

通过事务级触发器(或在服务层强制)保证每个 entry 提交前守恒。

9.3 外键与事件日志

支付事件 → 分录的映射通过 business_type + business_id 字段建立软外键(真正的外键跨表跨库难)。每一笔分录都能回到业务事件是审计的基石。

9.4 生成列与物化视图

  • balance 列可以用生成列 + 窗口函数做派生(极少用,代价高);
  • 账户余额普遍走物化:
    • 单独的 account_balances 表,事务内同步更新;
    • 定时 reconcile:SELECT SUM(direction * amount) FROM postings WHERE account_id = ? 对账验证;
    • 如果不等立即报警。

十、审计、WORM 与哈希链

十.1 Append-Only 是底线

账务表必须禁止 UPDATE / DELETE(余额表例外,但余额是物化,可以重算):

  • 应用层:DAO 层严禁写 UPDATE/DELETE SQL;
  • 数据库层:对 postings 不授权 UPDATE/DELETE;
  • 只允许 INSERT + 红冲:任何更正通过新插入一笔 reversal_of 指向原分录的反向 entry 体现。

十.2 数据库内审计日志

  • PostgreSQLpgAudit 扩展;
  • MySQLaudit_log 插件(企业版)或 Percona Audit Plugin
  • Oracle:Unified Audit;
  • OceanBase:内置审计,可开启 SQL 级审计到独立表;
  • TiDB:企业版审计 / 开源侧靠 general log + 处理。

审计日志要独立存储(不要和业务同库),保留与账务一致的年限。

十.3 哈希链 / Merkle 日志

对高合规要求(证券、跨境、数字资产)系统,除了 append-only 之外,还要能对外证明”这段历史没被偷改过”:

hash_i = H(posting_i || hash_{i-1})

每日终结时把 hash_N 写入独立存储(公证、区块链、OSS WORM)。任何历史数据被改过,重算哈希会对不上。

工程实现:

  • 分片内部各自哈希链;
  • 日终聚合生成”账务 Merkle root”;
  • 可选:上链到联盟链(长安链、蚂蚁链、Hyperledger Fabric)做第三方存证。

10.4 BTG / Before-The-Gate

“事件进账务之前的门”。任何要写账务的请求必须先过 BTG 记录:

  • 请求元数据(来源、业务 ID、签名、版本)落到 ledger_requests
  • 账务服务内部幂等键 = ledger_requests.id
  • 账务处理失败 / 超时 / 回滚都可以从 BTG 重放。

BTG 本身也是 append-only + 哈希链,保护”账务请求”这一层。


十一、真实案例演进

11.1 蚂蚁集团:从 MySQL 分库分表到 OceanBase

公开资料(蚂蚁技术白皮书、阿里云栖大会演讲)显示的演进:

  • 2004–2010:Oracle 为主,支付宝核心交易、账务在 Oracle RAC;
  • 2010–2013:推行”去 IOE”,账务逐步迁 MySQL + 分库分表(自研 TDDL / DRDS),单元化架构落地;
  • 2014 起:OceanBase 承接交易账务,2017 年双十一完全支撑核心交易;
  • 2019:TPC-C 707 万 tpmC(官方审计);
  • 2020 后:OceanBase 4.x 走向 HTAP,开始承接分析型场景。

关键工程经验:

  1. 账务服务化:业务不直接操作数据库,走”账务中心”服务;
  2. 单元化:同一用户的账户、订单、分录在同一单元;跨单元弹性切换;
  3. 分布式事务 GTS:上层统一调用,底层 2PC + 时钟;
  4. 三地五中心:RPO=0、RTO 分钟级;
  5. 对账是工程的一半:任何两条链路之间日终对账是硬要求。

11.2 PayPal:Couchbase + Oracle

PayPal 公开分享过账务架构:

  • Oracle 承担主账本:长期稳定、运维成熟;
  • Couchbase 承担高并发缓存与只读副本:手续费池、热点账户的读流量分摊;
  • Kafka 作为事件总线:账务变更事件分发到风控、报表、对账。

启发:不是所有场景都要”一个数据库解决所有”。热点读可以出库,主账本保守稳妥。

11.3 Stripe:自建 Ledger

Stripe 工程博客(“Designing robust and predictable APIs with idempotency”、“Online migrations at scale”)披露:

  • 早期 PostgreSQL 做账本;
  • 自建 Ledger 服务封装 double-entry 语义;
  • 演进出分布式 Ledger,支持全球多法域、多币种;
  • 核心原则:账务 API 幂等、append-only、每条记录可追到源事件

11.4 Square / Block:MySQL 分库 + 自研 Subledger

公开演讲(QCon、SREcon)描述:

  • MySQL 分库分表做主账;
  • Subledger 层做业务账 → 总账映射;
  • Reconciliation Service 日终对账、差错处理;
  • 逐步把部分业务迁到分布式数据库(2023 后引入 Vitess)。

11.5 传统银行:Oracle / DB2 + 分布式改造

中国四大行、股份行在过去十年普遍走”核心系统分布式改造”路线:

  • 工行:ICBC-M 核心 + OceanBase / GaussDB;
  • 建行、中行:部分核心走 TDSQL / GaussDB;
  • 招行:Oracle 长期稳定 + 部分 TiDB 试点;
  • 交行、中信:OceanBase 金融级落地。

监管侧信通院《分布式数据库发展报告》、银保监会《金融机构信息科技外包风险管理办法》为背景推动力。


十二、部署架构图

一张标准的”两地三中心 + 异地异步 + 冷归档”账务集群:

账务数据库部署架构(两地三中心 + 异地异步 + 冷归档)

同城机房 A(主)

Leader shard 0..N/3

Follower read-only

Sharding Proxy / SQL Gateway ShardingSphere / TiDB Server / OBProxy

Ledger Service(账务服务层) 复式约束 / 幂等 / 热点拆解

BTG: ledger_requests(哈希链)

同城机房 B(同步副本)

Follower Paxos/Raft

Follower read-only

Standby Proxy 健康检查 + 秒级切主

Ledger Service(热备) 单元化容灾

TiFlash / OB 列存(温层)

同城机房 C(见证节点 / Arbiter)

Paxos Voter(只投票不存数据) 避免双机房脑裂

监控 / 审计 / 配置中心 Prometheus · ELK · etcd

对账 / 报表 走温层 / 冷层 Trino · Spark · Flink Doris / StarRocks

sync vote

异地机房(异步复制 · 灾备)

异地副本集 CDC + 逻辑复制 RPO ≤ 秒 · RTO ≤ 分钟

异地 Ledger Service 只读 → 切主能力 演练季度一次

异地监控 / 对账 与主中心独立告警

冷归档(7–30 年 · WORM)

S3/OSS + Object Lock Iceberg + Parquet 哈希链 + 第三方存证

async CDC

ETL 归档

部署约束: · 同城三机房(A/B/C)Paxos/Raft 多数派同步,A+B 任一机房全损不丢数据; · 同城 RPO=0,同城单机房故障 RTO 秒级;异地机房异步复制,RPO 秒级 RTO 分钟级(用于极端灾难); · Sharding Proxy / Ledger Service 按单元化部署,路由表同步在配置中心(etcd / 配置平台); · 冷归档走对象存储 WORM,哈希链第三方存证(联盟链 / 公证机构); · 见证机房部署见证节点 + 监控 + 审计中心,与业务链路物理隔离。

十三、工程坑点

  1. 分片键拍脑袋:上线才发现 80% 流量集中在 20% 分片;
  2. 跨分片事务滥用:业务随手写跨库 join,性能塌方;
  3. 自增主键:分布式下热点 tail write;改 Snowflake / AUTO_RANDOM;
  4. CHAR vs VARCHAR vs NUMERIC 不统一:金额精度丢失灾难;
  5. 长事务:批处理跑了 30 分钟阻塞 VACUUM;
  6. 索引失效WHERE DATE(ts)WHERE ts::DATE、函数包裹;
  7. 只建主库索引:从库回放慢导致复制延迟;
  8. 在线加索引不 CONCURRENTLY / 不 ALGORITHM=INPLACE:锁表几分钟业务全挂;
  9. FOR UPDATE NOWAIT 没兜底:重试风暴;
  10. 余额物化表不对账:长期小差错滚成大事故;
  11. DDL 变更没评审:删字段、改类型直接上生产;
  12. 备份恢复没演练:真出事才发现备份没校验;
  13. 审计日志和业务同库:一起坏一起丢;
  14. 冷归档没有校验:迁移程序 bug,几百万行静悄悄丢了;
  15. 依赖云厂商单一区域:跨 region 切换演练缺失。

十四、选型与落地清单

14.1 选型建议表

场景 日分录规模 推荐
初创支付 / 单一国家 < 1 亿 PostgreSQL + 分区 + 同步副本
高速增长互联网金融 1–10 亿 TiDB 或 MySQL 分库分表
大型支付 / 银行核心 10 亿+ OceanBase / 国产金融级数据库
全球化跨境支付 跨境 CockroachDB / 多集群 + 异步
加密 / 交易所内账 高并发 TigerBeetle + 关系型主账本
存量传统银行 稳定 Oracle + 分布式改造

14.2 落地 Checklist


十五、本文小结

账务数据库选型和设计的决策链条是这样的:

  1. SLA 先行:RPO=0、RTO<1min、保留年限决定副本协议与存储分层;
  2. 模型锁定:复式记账的 append-only 语义是底线;
  3. 数据库挑选:看规模、看生态、看案例;PostgreSQL 进、MySQL 分、OB/TiDB 扛大规模、TigerBeetle 特种;
  4. 分片维度:以 user_id / merchant_id 为主,时间为辅,尽量让事务本地化;
  5. 热点账户:分桶 + 合并 + 事件溯源,几乎一定要做;
  6. 索引(account_id, time DESC) + 覆盖索引 + partial + 分区本地;
  7. 并发:分布账户用悲观锁,热点账户用分桶 + 乐观 CAS;
  8. 分层:热 OLTP + 温列存 + 冷 Iceberg WORM;
  9. 审计:append-only + DB audit + 哈希链 + 第三方存证;
  10. 演练:切换、恢复、归档、红冲每季度跑一次。

下一篇我们进入幂等、事务与一致性,把”一笔请求进到账务服务”的全链路幂等、SAGA、TCC、对账补偿讲透。


参考资料


上一篇《复式记账工程化:科目、分录、余额、对账》

下一篇《幂等、事务与一致性:SAGA、TCC、对账补偿》

同主题继续阅读

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

2026-04-22 · architecture / fintech

【金融科技工程】复式记账工程化:科目、分录、余额、对账

把 500 年历史的复式记账翻译成工程师可以落地的数据模型、SQL 表结构与余额计算策略,覆盖充值、下单、退款、分润、红包、多币种与冲销的真实场景,并对比 TigerBeetle、beancount、Ledger CLI、Square LedgerDB、Stripe Ledger 等开源与工业实现。

2026-04-22 · architecture / fintech

【金融科技工程】金融级可靠性:两地三中心、单元化、RPO/RTO、灰度

金融系统的可用性不是 SLA 表里的一个数字,而是人民银行、银保监、GB/T 20988 六级灾备、SOX、FFIEC 这些监管框架共同压出来的工程形态。本文从 RTO/RPO 的定义出发,走过单机→主备→同城双活→两地三中心→三地五中心→单元化(LDC/Set)的架构演进,拆解异地多活的数据同步、冲突处理与流量调度,配套混沌工程、全链路压测、应急预案与复盘文化,最后用 Go 写一个可运行的多活流量切换骨架。结合光大 8·16 乌龙指、2021 AWS us-east-1 故障、2020 工行手机银行故障、蚂蚁春节红包等公开案例展开。

2026-04-22 · architecture / fintech

【金融科技工程】金融科技工程全景:从支付到交易所的系统分类与读图

金融科技(FinTech)不是普通后端加一张账户表。钱的原子性、监管的硬边界、一个小数点的代价,把这个领域推进到工程强度最高的那一档。本文是【金融科技工程】25 篇的总目录与阅读地图:先交代为什么它比一般业务系统更难,再给出对账体、支付体、交易体、风控合规体四维分类,把后续 24 篇挂到骨架上,最后给出一份绿地项目的落地顺序建议。