LabHub
学习 学习路径 课程

银行现场的语言

银行为什么不去 UPDATE 余额

在 LabHub 中继续学习

一句话总结

银行核心不是修改余额使用,而是积累和汇总交易——因为只要保管余额,当数字不正确时,就没有世界上其他可以对比的对象了。

概念图: 无法追溯 · 不是账簿 · 所有交易都记录在两行以上,变数金额的总和和变数金额的总和必须相同。 · 存储的值,而是诱导的值

为什么需要这个?

在一般的服务中处理余额的方法很简单。UPDATE users SET balance = balance - 1000 WHERE id = 7. 一行,快捷,也很方便阅读。

这种方式的问题不是性能,而是无法追溯。有一天,接到余额异常的咨询时,我们手中只有现在的数字一个。要判断这个数字是否正确或错误,必须与在其他地方计算的值进行比较,但没有比较的对象。有人说要翻看日志,但日志有保存期限,格式各不相同,最重要的是不是账簿

银行早在几个世纪前就解决了这个问题。答案是复式制。

怎么行动

复式会计的规则只有一条。所有交易都记录在两行以上,变数金额的总和和变数金额的总和必须相同。

차변(Debit)   자산이 늘거나, 부채가 줄거나, 비용이 생기는 쪽
대변(Credit)  자산이 줄거나, 부채가 늘거나, 수익이 생기는 쪽

如果顾客存入100万韩元的现金,就会这样记录。

전표 JE-20260825-0001  (입금)
  차변  1010 현금          1,000,000
  대변  2010 요구불예수금   1,000,000

从银行的角度来看,现金这一资产增加了100万韩元(变动),同时,必须将这笔钱还给客户的义务——存款这一负债增加了100万韩元(代言)。银行存款不是银行的资产,而是负债——这是第一次看银行系统时最容易弄混的地方。

对账单(journal entry)是一笔交易,分行(journal line)是构成对账单的各行。一个对账单上附有三行以上的情况很常见。在支付存款利息时扣除源泉税时,在差变一行上附有两行。

在这个结构中,余额不是存储的值,而是诱导的值。某账户的余额是将该账户所附的所有分销行为合计起来得出的。而且将所有账户合计起来的表格是试算表(trial balance),试算表的差变总和和代变总和必须相同。

这个等式是银行系统拥有的唯一自我验证装置。即使每天有数百万笔交易进来,也可以用一条线条总结下来,问“0人签发”。

现实的核心是由于性能的原因,每个账户都同时持有余额栏。因为每次查询时无法累计数十亿笔。但那个栏是院长的副本,不是原件。如果两者不一致,院长总是获胜。

在现场相遇的样子

第一,**正正是逆分开,而不是UPDATE。**如果发现错误的凭证,不是修改那个凭证,而是把原始凭证像镜子一样倒过来的一张凭证(逆分开)放进去抵消,然后重新登记正确的凭证。原始凭证会剩下三张并排。这看起来很麻烦,但这是两个月后在审计中回答“这个凭证原来是这个金额吗”的人的唯一方法。在原始凭证上有UPDATE的痕迹的系统本身就是审计的注意事项。

进行逆分开时,**金额应按记录的内容填写。**如果将错误的金额原样翻转,错误就会完全抵消。如果说“反正错了,就用正确的金额翻转吧”,那么那一刻的差异就会只抹去一半,剩下的另一半则不会记录在任何地方,而是留在账簿中。

第二,**余额不符的咨询原因通常是这三者中的一个。**原因是会计师计算错误,余额副本错误,或者两者都正确,但看的时间不同。这三者对应完全不同。如果会计师计算错误,必须填写更正单,如果副本错误,会计师重新计算并盖章就可以了,如果时间问题,什么都不改,只说明即可。不告诉对方是哪一个,然后动手是最昂贵的错误。

第三,**只看余额栏的调查必须禁止。**余额一个只是数字。数字本身无法判断是否正确或错误。另一方面,汇总的账目值可以与余额栏进行比较,对账单单位的差额合计本身可以进行验证。调查时,打开哪个表格先决定当天几点下班。

将元长放在数据库中的规则

将复式部记转为表格的方法有很多种,但后面不会后悔的形式基本上是 一样。

**分歧在一个交易中有两行以上,和为0。**将变量和对应变量放在不同的表格中或 如果在一行中输入两个金额,总有一天只会输入一边。**在同一表格中输入多行, 让数据库确认那个组的和是否为0。

create table entries(
  id           bigserial primary key,
  txn_id       uuid not null,
  account_id   bigint not null references accounts(id),
  amount_minor bigint not null,        -- 차변은 양수, 대변은 음수
  currency     char(3) not null,
  posted_at    timestamptz not null,
  business_date date not null);

create index on entries(txn_id);
-- 묶음의 합이 0이 아니면 커밋을 막는다(지연 제약 또는 트리거로).

既不更新也不删除。错误输入的留言不是要删除,而是反对 再放一个分叉来抵消,把两个束子互相连接。这样才能“在某个时候 余额是多少”总是可以回答的。

**余额存起来,但用结算方式验证。**每次加起来都很慢,所以单独存余额。 虽然会一直拿着,但那个价格和分开的合计一定会有不一致的瞬间。在一定时间点的 以快照+之后的愤怒的形式留下来,在夜间再加上整个内容进行对比。

**金额是最小单位的整数。**韩元是1,美元是分。不使用小数点。 如果打了多次电话,绝对不会把金额和电话分开——把电话中的两个金额 如果有增加的代码,那就是错误。

**将工作日期和开盘时间分开。**凌晨1点入场的交易属于前一天营业日。 可以。如果捆成一团的话,截止日期的数字因人而异。

**同时性以账户为单位计算。**如果同时在同一账户上记入,余额就会不一致。 账户行select ... for update或将其归类,或将余额完全视为分开的合计。 选择不设置余额行的设计。

下次实习要做的事情

亲自制作三天账簿的快照,在其中找到隐藏的1个车代不一致。从实算表开始,缩小到一张全表,甚至到分开行的一行,通过余额栏和账簿合算,确认两者的性质为什么不同。最后,不动账簿,直接用逆分开和再起表纠正。