专业书籍精读 · System Design Interview Vol 2 · 第 12 章

设计数字钱包

System Design Interview Vol 2 · Ch 12 · Alex Xu & Sahn Lam · 2022

EN →

这一章讲什么?

你在支付宝里给朋友转 1 块钱,或者在游戏里把金币送给队友——这一章讲的就是这条转账背后那本账怎么记。它的核心问题很反直觉:难的不是「转得快」,而是「一年以后你还能说清,这 1 块钱当时为什么少了」。

先打个比方

把钱包想成一个存折,记法有两种。

一种是本子上只写一个数字:现在有 100 块,转出去 1 块,就把 100 擦掉改成 99。省地方,一眼看得懂,但擦掉的东西永远找不回来了。

另一种是只往下记流水:「3 月 1 日,转出 1 元」「3 月 2 日,收到 5 元」——余额那一栏干脆不写。要知道现在有多少钱,就把流水从头加一遍。啰嗦,但任何一天的余额都能重新算出来,而且谁来算、算几遍,结果都一样。

这一章的终点,就是把第一种换成第二种。

旧世界为什么难

两件事同时难。

其一,转账要「同时」发生:A 的钱少了、B 的钱多了,这两下得算一次动作。可要是中间断电了呢?何况两个人的账往往根本不在同一台机器上。

其二,钱是要被追问的。对不上账的时候、用户投诉的时候、监管来查的时候,你得说得出「那一刻余额是多少、被哪几笔改成这样的」。只写一个数字的本子,答不了这个问题——它只记得现在,不记得为什么。

核心机制直觉

这一章分三步把问题解掉。

第一步,先让「同时」成立。做法像餐厅订座:先把 A 的 1 块钱冻结——钱还挂在 A 名下,但花不出去了;再去 B 那边占好一个入账位;两边都答应了,才真正落定。中途谁反悔,就把冻结解开、位子撤掉。座位占住不等于吃上饭,但至少不会两桌人同时坐进去。

第二步,把「擦掉重写」换成「只往下记」。每一笔都是一条永不修改的流水。余额不再是存起来的东西,而是算出来的东西。为了不用每次从创世那天加起,隔一阵拍一张「快照」记下当天余额,之后只加新流水就行。

第三步,让这本流水别丢。同一本流水同时抄给三台机器,每一笔要三台里至少两台点头,才算真的写上了。坏掉任意一台,剩下两台手里的账本一模一样,接着记就是了。

该怎么选

如果你系统里流动的是钱、是库存、是任何「事后会被追问」的东西,这套记法值这个麻烦。代价也很实在:流水只增不减,存储会一直长,而且写错了不能改,只能再记一笔反向的把它冲掉——对习惯了「改一下数据库那行就完了」的团队,这是个不小的转变。

一句话记住

数字钱包的终局是:余额不再是一个被改来改去的数字,而是一串永不修改的流水加出来的结果。正因为它是算出来的,它才能被重新算一遍以自证清白,也才能抄给好几台机器而不走样。

想进到具体机制、记号和示意图? → 切到精读版