编程 Debit/Credit 借贷记账原语在支付账务系统中的应用:热点账户、先借后贷与对账的工程笔记

2026-09-01 00:10:10

Debit/Credit 借贷记账原语在支付账务系统中的应用:热点账户、先借后贷与对账的工程笔记

从一个压测问题说起

账务系统压测,单账户记账接口的耗时大约在几十毫秒量级,算下来 TPS 只有 30 出头。如果是热点账户,比如一个大商户的待结算账户,并发一来就是几十笔,单账户直接打满。这就是 Debit/Credit 在支付系统落地时第一个绕不开的问题:原语很简单,但账户一旦成为单点,就不够用。

复式记账:账不能只记一条

复式记账的核心是:每笔资金变动至少记两条账,一借一贷,借贷必须相等。账户分资产类和负债类。支付平台对用户的欠款是负债类账户,放到央行备付金是资产类账户。借贷相平不仅是对账的依据,也是账务系统能自证清白的根基。

账务流水至少要包含:账户标识、借贷方向、金额、业务单号。示意:

-- 示意:账务流水表
account_id   VARCHAR(32)
direction    ENUM('DEBIT', 'CREDIT')
amount       BIGINT  -- 单位:分
biz_no       VARCHAR(64)

账户体系:客户账户、内部账户和 B/C 账户

支付平台的账户不是一张表那么简单。至少要分:

  • 客户账户:对私/对公。
  • 内部账户:头寸、手续费收入、过渡户/中间户。
  • 银行账户:虚拟总账账户。这个账户不记余额,只记流水,目的是跟银行对账,避免单边账导致借贷不等。

商户账户里有个常见设计:B 账户(待结算/中介账户)和 C 账户(现金账户)。用户支付的钱先进 B 账户,结算完成后转到 C 账户,C 账户里的钱才能提现。如果业务要支持单笔实时结算,B 账户可以取消——原文里举的例子是微信境外收单。

热点账户:缓冲记账和多账户

单账户记账受数据库性能限制,几十毫秒、约 30+ TPS,高并发场景不够用。原文给了两条路:

  1. 缓冲记账:先记流水,定时汇总记账。只把入账(贷)方向合并。
  2. 多账户体系:把业务压力均摊到多个数据库记录上,分「功能分离型」和「功能完整性」两类。两类具体怎么做,原文未展开。

关键边界是:出账必须实时,所以只能对入账做合并。入账可以晚一点汇总,出账等不起。

先借后贷:拆成两个本地事务

账务系统通常不敢碰分布式事务。原文的做法是:同一个事务里「先借后贷」,拆成两个本地事务。也就是不用一个跨库事务同时改两边账户,而是先本地记借方,再本地记贷方。中间状态靠过渡户/中间户表现。

示意:

# 第一笔本地事务:记借方
with local_tx(account_debit):
    insert_ledger(account=account_debit, direction='DEBIT', amount=amount, biz_no=biz_no)
    # 余额更新按账户类型走,不赘述

# 第二笔本地事务:记贷方
with local_tx(account_credit):
    insert_ledger(account=account_credit, direction='CREDIT', amount=amount, biz_no=biz_no)
    # 余额更新按账户类型走,不赘述

两个本地事务之间如果失败,靠对账和过渡户把账拉平。这是应用层绕开分布式事务的一种典型取舍。

对账:三类对账

对账是账务系统的安全网。分三类:信息流对账、账单对账、账实对账。对账结果会区分长短款,具体口径原文未提供。银行账户「不记余额只记流水」这个设计,就是为了让系统流水能跟银行账单逐笔对上。

携程账务中台:场景码与原子系统

携程账务中台把账户开立、记账、稽核抽象出来。核心模型是场景码:产品代码 + 交易类型,用来定义交易顺序、关联子账户。账务原子系统本身没有业务逻辑,由同步执行器和异步执行器驱动。也就是说业务加一个新场景,不需要改账务核心,配场景码就行。

三阶段:清算、结算、对账

整个清结算系统可以分成三个阶段:

  • 清算:算应收应付。
  • 结算:资金划拨。
  • 对账:核对账实。

和 TigerBeetle 文的差异

站内那篇 TigerBeetle 讲的是数据库原语:把 Debit/Credit 做成存储引擎里的转账原语,调用方提交账户、金额,引擎保证原子性和不变量。数据库管的是「这笔转账怎么原子落库」。

本文讲的是账务系统应用层:数据库之上怎么建模账户、怎么拆 B/C 账户、怎么用缓冲记账扛热点、怎么用两个本地事务模拟先借后贷、怎么对账。应用层管的是「这笔钱该不该记、记到哪个账户、记完怎么跟银行对上」。

一个是地基,一个是上层建筑。TigerBeetle 保证「A 扣一笔 B 加一笔是原子的」,应用层决定「这笔业务到底是走 B 账户还是 C 账户,要不要缓冲」。

边界与不适用场景

这套设计有明显的边界,反过来也说明哪里不适合用它:

  • 缓冲记账只合入账,出账必须实时。如果业务要求所有方向都强实时,缓冲套路不成立。
  • 账务系统是原子系统,不应该塞业务逻辑。把营销规则、优惠计算写进记账系统,稽核会很难受。
  • 复式记账适合需要资金安全、对账、审计的场景。纯积分、内部计数、无对账要求的场景,用借贷记账平白增加复杂度。
  • 原文未提供:多账户体系两种类型的具体展开、微信境外收单取消 B 账户的完整流程、长短款的统一定义。真到落地时,这些要靠具体业务设计和压测数据补齐。

推荐文章

程序员茄子在线接单