专业书籍精读 · Database Internals · 第 13 章

分布式事务

Database Internals · Ch 13 · Alex Petrov · 2019

EN →

这一章讲什么?

你点一下「转账」,钱从你的账户扣掉、加到对方账户上;要是这两笔只成了一笔,就是灾难。单台数据库里这事早就解决了——这一章问的是更难的版本:当两个账户根本不在同一台机器上,怎么保证「要么都成、要么都不成」?这就是分布式事务(distributed transaction)。

先说个怪事

想象三个人在三个城市要签同一份合同,必须全签或一个都不签,而他们只能打电话。常规办法是找个牵头人跑两轮:第一轮挨个问「你能签吗」——谁回了「能」,就等于把笔攥在手里、位子占住了,不许再反悔;第二轮收齐「能」之后,挨个说「签」。

怪事就在这两轮之间。牵头人收齐「能」、还没通知任何人之前,手机掉进了水里。剩下两人签也不敢(万一他其实决定撤销?)、撤也不敢(万一有人已经签了?),只能攥着笔、占着会议室干等。

旧世界为什么难

这不是办法笨,而是绕不开。在剩下两人看来,「牵头人没做决定」和「做了决定但我没收到」长得一模一样,只要第二种可能还在,谁也不敢自己拍板。于是这套办法有个硬伤:保证绝不出错,但不保证一直能动。在数据库里,「攥着笔干等」就是那几行数据被锁着、别人读不了也写不了,而牵头人重启可能要几分钟。

三条不同的出路

多打一个电话有用吗?有一点:牵头人先通知「我要让你们签了」,之后谁等不到下文就默认签。但电话线要是从中间被掐成两半,两边各按默认行事,就可能一半签了一半撤了——比卡住更糟。真正管用的三招都不是改进那两轮,而是换地基:

① 先排号,再办事。不问「能不能」,先把所有人要办的事排成一个公认的号码顺序,各自照号办。顺序一样、办法一样,结果必然一样——不用商量。

② 牵头人不止一个。让这个角色由一小组人共同担任,决定一做出来组里好几个人都记着;掉一个手机不影响大局。

③ 把「谁签了」写在合同本身上。约定以某一页的签名为准,那页签了整份就生效;后来翻到的人顺手把其他页补齐或划掉——不需要全程在场的牵头人。

该怎么选

三招都要付学费:排号法要求你事先把要干的事全交代清楚,不能边做边想;多牵头人法要靠极准的时钟和更贵的机房。所以工程上常有个更好的第四答案:先看看这事需不需要凑成一个事务。

一句话记住

跨机器的「要么全成要么全不成」,难点在于必须先让大家承诺不反悔,才能做决定——而承诺之后、决定到达之前那一小段,牵头人一倒,所有人只能攥着锁干等。后来的办法全在换地基:先排号、让牵头人变成一群人、把状态写进数据本身,或者别开这个事务。

想进到 2PC / 3PC 的具体协议、Calvin 与 Spanner 的地基差别和示意图? → 切到精读版