卷二:分布式的沼泽 (The Distributed Hairball)
核心主题:拆分带来了网状依赖的噩梦,不可控的级联故障逼迫主角寻找真正的隔离舱(Cell)。 时间跨度:2005 - 2014
第 12 章:无情的光缆挖掘机 (The Backhoe and the Split-Brain)
2012年5月,美国中部堪萨斯州(Kansas)。一台轰鸣的小松挖掘机。
自从 2004 年硅谷那场大停电(第10章)把创世软件的心脏烧成灰烬后,西拉斯·霍恩就对“物理单点故障”产生了极深的创伤后遗症(PTSD)。
为了向华尔街保证 Hello World 永远在线,西拉斯批下巨额预算,在美国东海岸的弗吉尼亚州阿什本(Ashburn,全美最大的数据中心枢纽)建立了一个同样规模的巨型数据中心。
“如果加利福尼亚州沉入太平洋,”西拉斯在年度大会上对着无数投资人喷着唾沫星子,“东海岸的机房,也将在一秒钟内接管所有的流量!这就是世界上最伟大的双主(Active-Active/Master-Master)跨地域容灾!”
这套架构听起来无比性感。 加州机房和弗吉尼亚机房,相隔着近 4000 公里的北美大陆。这两个机房各自拥有独立的微服务网关和数据库主节点。当西海岸的用户发帖时,数据写在加州;当东海岸的用户点赞时,数据写在弗吉尼亚。 然后,这两个极其庞大的数据库,通过一条横跨美国的极速地下光纤专线,在毫秒之间疯狂地进行着双向双写同步(Bidirectional Replication)。
在李思的通感(Synesthesia)视界中,这两个相隔千里的数据中心,就像是一个人类大脑的左半球和右半球。那条横跨美国大陆的高速光纤,就是连接左右脑的胼胝体。左右脑通过这根粗壮的神经,分享着共同的记忆。
但在李思看来,这套被西拉斯吹上天的架构,却散发着极其诡异的危险气息。
“你让两个相隔四千公里的皇帝,同时拥有这片大陆的绝对处理权。”李思曾警告过西拉斯,“只要那条光缆还在,他们还能互通书信,帝国就相安无事。但如果有一天,他们听不到对方的心跳了呢?”
“这不可能。”西拉斯摆摆手,“那是 Level 3 通信公司最高级别的军用级主干光纤!深埋地下三米!”
物理法则,专治各种不服。
2012年5月14日,下午 2:00 (PST)。雷德蒙德战情室。
堪萨斯州,一个名不见经传的小镇。一名粗心大意的外包施工队挖掘机驾驶员,为了铺设一条下水管道,一铲子狠狠地挖了下去。
“咔嚓。” 伴随着极其沉闷的物理断裂声,那一根包含着 144 芯、承载了北美 20% 互联网跨骨干通信的军用级主干光纤……被一分为二。
远在万里之外的雷德蒙德,战情室的大屏幕毫无征兆地从中间裂开了。
极其尖锐的警报声淹没了所有人的交谈。
“发生了什么?!”西拉斯刚在喝水,差点呛到。
“加州机房的数据库,报告说他们彻底失去了弗吉尼亚机房的心跳(Heartbeat)!”运维总监戴夫疯狂地敲击着键盘,看着满屏的 Timeout 红字,“加州的监控系统判定,东海岸弗吉尼亚集群……已经全体阵亡!”
“那弗吉尼亚那边的监控呢?!”
“等等……我用卫星备用链路连上了弗吉尼亚,”戴夫的脸色变得极其苍白,“弗吉尼亚的监控系统报告说……加州机房的心跳丧失!弗吉尼亚判定,西海岸加州集群……全体阵亡!”
战情室里鸦雀无声。
李思闭上双眼,一股难以名状的撕裂感直击他的天灵盖。 在通感视界中,那个庞大无比的虚拟半透明数字大脑,中间那根粗壮的“胼胝体”被极其野蛮地一刀斩断。
最恐怖的事情发生了。大脑的左半球(加州)和右半球(弗吉尼亚)都还极其健康地活着,但是,它们互相听不到对方的心跳了。
这就是分布式系统中最令人闻风丧胆的梦魇——网络分区(Network Partition)。
“启动自动故障转移机制(Auto-Failover)!”西拉斯反应极快,这套昂贵的脚本正是为了应对一方死亡而设计的。
“已经……已经自动启动了……”戴夫的声音在颤抖。
“太好了!”西拉斯松了一口气,“既然加州以为弗吉尼亚死了,那加州应该接管全网流量;弗吉尼亚也会这么做。我们不会有哪怕一秒钟的宕机,对吧?”
“蠢货!!!”
李思猛地睁开眼睛,一拳砸在主控制台上,几乎要把键盘砸碎,他的双眼因为暴怒而充血。
“你还不明白吗?!加州以为弗吉尼亚死了,所以加州的代码把自己强行提升为了全网的唯一主节点(Primary),它开始无条件接收周边所有网民的写请求!” 李思指着大屏幕上那代表着弗吉尼亚的数据流,“但是,弗吉尼亚也以为加州死了!弗吉尼亚的代码也把自己提升为了唯一的主节点,它也在疯狂接收东海岸网民的写请求!”
李思的声音在诺大的战情室里回荡,带着彻骨的寒意。 “这两个都活着的机房,现在彻底失去了沟通。它们正在各自为战地修改数据!这就是精神分裂!这就是——脑裂(Split-Brain)!”
西拉斯愣住了:“脑裂……会怎样?”
“会创造两个互不相容的平行宇宙。”
李思将手指指向监控面板上的一个真实的普通用户数据。
“看看这个用户,他叫鲍勃。他现在账上有 100 美元。 一分钟前,鲍勃在纽约(连着弗吉尼亚机房),用这 100 美元买了一张游戏月卡。因为中间光纤断了,弗吉尼亚机房把他的余额扣到了 0。但这笔扣款记录,无法同步给加州机房。 现在,加州机房里的鲍勃,账上依然是 100 美元。更要命的是,鲍勃的妻子在洛杉矶(连着加州机房),刚才登录了同一个账号,把那 100 美元提现了!加州机房也把余额扣到了 0!”
因为在两个相互隔离的独立宇宙里,他们都拥有全部的可用性(Availability),他们都觉得刚才的交易是极其合法的!
“等光缆修好的那一刻……”李思转过头,看着满脸煞白的西拉斯,“两个宇宙重新拼接。当加州要把提现记录发给弗吉尼亚,弗吉尼亚要把消费记录发给加州时……这 100 美元,到底花在了哪里?谁对?谁错?”
西拉斯彻底陷入了冰窖。
在单体时代,数据库就算死锁,也绝不会撒谎。 而现在,系统为了追求“永不宕机”的可用性(A),在物理光纤断裂(P)的那一刻,它向最神圣的系统一致性(C)刺出了致命一刀。
它撒谎了。它创造了百万条互相冲突的脏数据。
四个小时后。堪萨斯州的工程队冷汗淋漓地接好了被挖断的光缆。 横穿北美大陆的通信恢复了。双向同步重连的那一微秒。
警报并没有解除,反而爆发出前所未有的凄厉惨叫!
“冲突!致命的数据冲突!”DBA 几乎要哭了,“主键重复!余额变成负数!双向同步进程因为遇到一百五十万条无法互相合并的脏数据,底层同步引擎直接宣告自身崩溃,彻底宕机挂起了(Halted)!”
“这怎么可能拼接得起来?”李思望着那满屏猩红的报错。 那个精神分裂的大脑被强制缝合了,但左右半球对这四个小时的记忆截然不同。系统陷入了比纯粹宕机更可怕的境地——无法信任的脏状态。
因为不想失去四个小时的营业额,他们换来了一百五十万条需要人类肉眼去一条一条辨别谁真谁假的脏记录。财务部和客服部将在接下来的三个月里面临人间炼狱。
战情室里弥漫着如丧考妣的死寂。
“这没有任何软件能够防御,对吗?”西拉斯颓然地坐在沙发上。
“是的。因为你是在试图从数学层面战胜物理学。” 李思在白板上,冷冷地写下了三个极其巨大的英文字母:C、A、P。
这就是后来由加州大学伯克利分校的 Eric Brewer 提出的、让无数分布式工程师绝望的绞肉机——CAP 定理。
“C,Consistency,数据的一致性。” “A,Availability,服务的可用性。” “P,Partition Tolerance,网络分区的容忍度(也就是光缆断裂)。”
李思重重地敲击着白板:“在分布式系统里,P(网线断裂)是一个你无法控制的物理客观事实!只要你的机房跨越了空间,就一定会被老鼠咬断、被挖掘机挖断、或者光纤交换机烧毁。”
“当 P 发生时,系统被一切为二。你只能在 C 和 A 里面,选且只选一个!”
李思指着西拉斯:“你刚才选择了 A(可用性)。你让两个断联的机房继续接客。结果呢?你彻底丧失了 C(一致性)。全网产生了一百万条互相精神分裂的冲突数据,后期修复这些脏数据的成本,甚至让你亏得倾家荡产!”
西拉斯咽了一口唾沫:“那如果……我们当时想要保住 C(一致性)呢?”
“那你要做出的牺牲同样残酷。”李思的眼神像刀子一样锋利,“如果要想保住数据绝对一致,在光缆断裂的那一刻,两边的系统必须极其痛苦地、瞬间自尽,拒绝全美国用户的所有修改请求(牺牲 A,也就是不可用),直到光缆修好为止。”
要么拥抱脏数据(脑裂),要么拥抱大停机。
“难道在这片分布式的沼泽里,就没有第三条路吗?”西拉斯绝望地抓着头发,“难道每一次网线断裂,我们都要面对这个二选一的死局?”
“有。”
李思将目光投向了白板。他擦掉了代表“西海岸”和“东海岸”的两个点,然后,他在白板的正中间,画下了第三个点。
“两个人的投票,永远会出现 1:1 的平局,这就是脑裂的物理根源。” “为了战胜精神分裂,”李思的眼中燃起了对极致架构的狂热,“我们要赋予这个分布式帝国,真正的民主。”
“奇数、多数派(Quorum)、以及最原始的三权分立。”
只有引入第三个观察者(哪怕它什么数据都不存,只拥有投票权)。当光缆断掉时,只有能联络上这个“第三者”并获得【2票】(多数派)的那个机房,才配叫做合法的主节点。 而那个不幸变成孤岛(只有 1 票)的机房,将在数学法则的约束下,被强制剥夺写权限,悲壮地“自行了断”。
这也正是卷二终局,未来那个伟大共识算法(诸如 Paxos/Raft)黎明前的曙光。
架构决策记录 (ADR) & 事故复盘 (Post-Mortem)
文档编号:PM-2012-05-14 事故等级:SEV-0 (跨地域集群脑裂,引发大规模脏数据与对账灾难) 主导人:李思 (Principal Engineer)
1. 事故现象 (What happened?) 施工队挖断了连接美西与美东双活数据中心的主干物理光纤。导致两个原本进行双向同步的主库集群失去了互相的心跳感知。集群的降级脚本发生严重误判,双边同时将自身提升为绝对唯一的 Primary(主节点)。系统发生极其恶劣的脑裂 (Split-Brain)。 在断网的 4 小时内,两地分别独立累积了超过 150 万条互斥的交易流水。光纤恢复后,底层同步线程试图对冲合并(Merge)时遭遇 Massive Unique Key Violation,全库彻底宕机。
2. 5 Whys 根本原因分析 (Root Cause)
- Why 1:为什么光缆断了系统没停,最后结局却比停机还惨? 因为在网络分区发生时,系统错误地选择了 AP(用可用性牺牲了一致性),导致了独立并发写。
- Why 2:为什么两个机房会双双变成主节点? 因为这套基于“双节点(Even Node 偶数节点)”的自动故障转移机制存在致命盲区。当 A 看不到 B 时,A 无法判断是 B 死机了,还是仅仅自己与 B 之间的网线断了。
- Why 3:为什么 A 无法判断? 由于缺乏全局仲裁者或者第三方的存在。2 个节点无法构筑超过半数的“绝对真理”。它们每个人都拥有 1 票,在失联时陷入了最极端的 1 V 1 僵局。
- Why 4:为什么同步恢复后数据库直接挂起? 关系型数据库(RDBMS)的物理底座是极度依赖顺序和强约束的。它不具备类似 CRDT(无冲突复制数据类型)或者 Git 那样可以通过魔法智能合并冲突的能力。对冲直接触发了底层的崩溃保护。
3. 解决方案与架构决策 (Action Items & ADR)
- 临时止血 (Workaround):关闭双城同步进程。手动调配几百名高级工程师与财务人员,根据时间戳、业务属性逐行鉴别 150 万条脏数据,进行长达数月的平帐与客诉理赔。
- 架构重构 (Long-term Fix):
- ADR-012:废除双节点的高可用集群(Even-node cluster),强制引入奇数节点与 Quorum(多数派)机制。
- 我们将在位于美国中部的德克萨斯州(Texas)建立第三个轻量级的控制层见证节点(Witness Node)。
- 全网只有在获得“总节点数/2 + 1”(即至少 2 票)的选票时,才能被承认为合法 Primary。当下一次主干网断裂,被切断的孤岛将因为无法拉拢德州的见证人而惨遭剥夺写权限(强制降级为只读或下线保证 C),从而极其优雅且冷酷地从数学层面彻底扼杀“精神分裂”的产生。
4. 爆炸半径与代价反思 (Blast Radius & Trade-offs) 盲目追求 100% 的高可用性导致了数据正确性的崩塌,这是分布式时代用血写成的教训。我们由此深刻认识到,只要是牵扯到数据库的底层写入,宁可宕机挨骂退钱(牺牲 A),也绝不能让系统在暗网中偷偷写入互斥的假数据(牺牲 C)。
架构师科普:连接过去与现在的系统设计 (Architect's Note)
1. CAP 定理(Brewer's Theorem)的无情绞肉机 这是每一次高阶分布式面试和系统设计的地基。
- C (Consistency - 一致性):你在这个机房写的数据,在另一个机房读出来必须是一样的,绝不能有时差。
- A (Availability - 可用性):你每次点“提交”,系统即使有几率返回缓慢,也绝不能直接抛错告诉你“我不干了”。
- P (Partition Tolerance - 分区容错性):即使两台机器中间的网线被老鼠咬断,系统作为一个整体,你还得给我接着演。
物理学的残酷在于:由于光速的限制(一秒 30 万公里)和硬件的老化,P(网络断层)必然发生,无法避免。所以架构师在设计系统时,根本不是在 CAP 中选两个,而是P作为前提定死的情况下,在 C 和 A 中极其痛苦地二选一:
- 选 CP 放弃 A:网线断了?为了保证你们看到的数据绝对不出错,我的两个机房直接拉闸关门!拒绝接客!(金融、支付、核心账务必须这么做)。
- 选 AP 放弃 C:网线断了?为了赚钱,两个机房硬着头皮各自独立接客!产生冲突数据?后期让程序员熬夜手写脚本或者用合并算法去擦屁股!(早期的发朋友圈、网页浏览等无生命危险的系统可以这么做)。
2. 脑裂 (Split-Brain) 与双数节点的诅咒 所有初尝分布式的公司,最容易犯的一个致命错误就是买两台光鲜亮丽的机器做“高可用双活”。这是大忌。 在两台机器的孤岛博弈中,一旦失联,两边都以为自己是王。这就引发了本章恐怖的“脑裂双写”。 现代的分布式核心组件(无论是后面的 ZooKeeper, etcd (K8s底层), 还是 Redis Sentinel)在配置高可用时,死律都必须是奇数个(3台,5台,7台)。这就引入了伟大计算机科学中的 Quorum(多数派投票法):3个人中如果因为网线断开变成了 1 对 2,那个孤零零拥有 1 票的节点会发现自己成了少数派,系统会自动剥夺它的王冠,强迫它自杀或者闭嘴。 奇数法则以及过半决议,是从混乱物理网络中提取唯一真理的最伟大数学利器。