故事

光绪年间,上海汇丰银行的分号开到了汉口、天津、广州三地。总账房设在黄浦江畔,各地分号每日把汇兑、放款、存取的数目抄成副本,用轮船或电报发回上海。上海核对无误后,再发回一份"照准回执",分号才能向客户交割。这叫"总号做主,分号照抄"——主从复制的雏形。

规矩很简单:上海总号的账簿是唯一正本,汉口只能读,不能改。汉口柜员若要放款,须把客户的押据、保人、期数写成"呈请",发船送沪。上海账房核对头寸、验过印鉴,在正本上落笔,再把"准行"的批复发回。一来一去,快则五日,慢则半月。汉口的客户急得跺脚,柜员只能赔笑:"总号核过才作数,这是铁规矩。"

铁规矩也有变通。光绪二十三年,汉口闹挤兑,存户把门槛踏破,分号的预备金眼看要空。按老例,须等上海批银子来,可船期赶不上。汉口大班一咬牙,从本地银号拆借十万两应急,事后才补呈请。上海账房大怒:擅自动用头寸,章程何在?汉口辩称:事急从权,若等批复,分号已经关门。双方各执一词,最后由总税务司赫德出面调停——汉口可以"预垫",但垫款须记"暂记"科目,待总号追认后方可转正

这是主从复制里的异步回授:从节点先斩后奏,主节点事后追认。好处是响应快,代价是主从之间可能出现"暂记"状态的灰色地带——总号若否决,汉口已经放出去的贷款怎么收?

更大的麻烦在光绪二十六年。义和团进了北京,电报线被砍,轮船停航。上海与天津、汉口断了音讯。天津分号手里攥着一叠"呈请",发不出去;客户要提款,柜员不敢自作主张。有人建议:天津干脆自己记账,等通路恢复再与上海对账。可谁敢拍板?万一上海那边同期也批了同一笔头寸给别家,两边账对不上,谁的责任?

天津停了业,把库门锁上,挂出"总号有命,暂停交割"的牌子。这是主从复制的同步僵局:从节点没有主节点的确认就不敢动,主节点一失联,整个系统停摆。有人私下议论:若当初让天津也有记账之权,至少能维持本地生意。可上海的总办摇头:记账之权一分,正本就不唯一了,日后对账无穷尽也

上海账房的老先生姓周,七十岁了,管了四十年账簿。他看出症结不在"谁有权记账",而在"什么都得问上海"。他向总办进言:寻常查询——余额、流水、过往契据——何必惊动总号?汉口、天津各自存一份"阅本",仅供查阅,不具效力;正本仍锁在上海,改一笔、批一笔。查询走阅本,交割走正本,两不相扰。

总办准了。于是各地分号有了两套簿册:正本锁在保险柜,等上海批复;阅本放在柜面,供客户查账。每日上海把更新的页数抄发各地,各地照样誊入阅本。这叫读从写主:写仍归上海,读可就近。

可周老先生没料到,阅本也有阅本的麻烦。光绪二十九年,上海批了一笔放款给汉口的某茶商,阅本已经更新;可那茶商同时也在天津分号押了一笔货,天津的阅本尚未收到更新,看不出他已在汉口负债。两地阅本不同步,茶商两头拆借,等到上海总号发现,窟窿已经大了。

周老先生叹口气,在章程里补了一条:阅本更新有迟滞,跨号查账须发专函上海核实。可这么一来,"就近查询"的便利又打了折扣。主从复制的天平,一头是一致性,一头是可用性,周老先生这辈子都在调这架天平,从没真正调平过。

概念解析

Primary–Backup Replication 是分布式系统里最古老的复制模式:一个主节点(Primary)负责处理所有写操作,一个或多个从节点(Backup/Replica)复制主节点的状态。从节点可以服务读请求,也可以纯粹作为容错备份。

这种模式的核心矛盾在周老先生的故事里已经尽显:读写分离能扩展读性能,但引入主从延迟;同步复制保证一致性,但牺牲可用性;异步复制提升响应速度,但容忍短暂的不一致。工程中的每一种变体——MySQL 的半同步复制、PostgreSQL 的流复制、Redis 的主从哨兵——都是在调这架天平的不同刻度。

主从复制的瓶颈最终落在主节点身上:所有写操作必须经它之手,它既是咽喉要道,也是单点故障。后来的链式复制、无主复制、状态机复制,本质上都是对这个瓶颈的回应——但回应的方式越复杂,一致性保证就越难维持。主从复制的价值,恰恰在于它用最小的复杂度,把这个根本张力摆在了台面上。