複製:單主、多主、無主,與延遲的三個怪象
· tech
#distributed-systems#book-notes#replication
進入 Part II,資料開始跨機器。第一招是複製(replication):同一份資料放多台,為的是三件事——一台掛了還能服務(高可用)、資料放得離使用者近(低延遲)、讀流量攤出去(擴讀)。如果資料不會變,複製就是複製貼上而已;所有的難,都難在「資料會變」——變更要怎麼傳到每一份副本? 我在 Redis 主從那篇拆過其中一種(單主、非同步)的實戰;這篇拉到 DDIA 的高度:全世界的複製方案,其實只有三種拓撲,而它們的差別,是「把寫入衝突放在哪裡處理」。
三種拓撲:衝突不會消失,只會搬家
無主那個 quorum 值得多一句:n 份副本,寫入要 w 份確認、讀取要問 r 份,只要 w + r > n(例如 n=3、w=2、r=2),讀跟寫的集合必有交集,你一定會碰到至少一份最新值(再從中挑新的)。數學很漂亮——但 DDIA 誠實地列了它的邊角:並行寫入的先後難定、寫入部分失敗不回滾、sloppy quorum 時保證會鬆動。它是機率上很強的工程保證,不是絕對的數學證明,這個分寸後面講共識時會再回來。
複製延遲的三個怪象:有名字,才能對症下藥
只要複製是非同步的(絕大多數都是,原因見 Redis 那篇的取捨),replica 就永遠慢半拍,於是會冒出各種「見鬼了」的讀取結果。DDIA 最有價值的貢獻,是給這些怪象起了名字——三個病,三種藥:
這三帖藥合起來,就是所謂的因果一致性的民間版本:不追求「全世界同步」,只保證「跟你有關的、有因果的部分看起來是對的」。多數產品要的其實就是這個——而它比強一致便宜太多了。
反思
三種拓撲,是「把衝突放在哪」的三種選擇
讀完這章,我把三種拓撲收斂成一句話:寫入衝突不會消失,只會搬家——你只是在選它搬去哪。 單主把衝突消滅在源頭(單一寫入點),於是把難處搬到了 failover(誰發現、誰接任、怎麼不裂腦);多主讓各地都能寫,把難處搬到了事後的衝突解決(LWW 會默默丟資料、合併邏輯是應用的痛);無主不搞 leader,把難處搬進了每一次讀寫的路徑(quorum、read repair)。這個「難處守恆」的視角,跟我在 infra 系列講「沒有真正無狀態的系統,只有把狀態推去別處的系統」是同一種思維。所以選複製方案時,我的問題從「哪個好」變成了「這三種苦,我的團隊最吞得下哪一種?」——多數團隊的答案是單主,因為 failover 的苦有 Sentinel、K8s、managed 服務幫你扛,而衝突解決的苦只能自己吞。
給怪象起名字,是這章最被低估的貢獻
read-your-writes、monotonic reads、consistent prefix——第一次讀會覺得是學術詞彙,但實戰過就知道:這些名字是「把玄學變工單」的把手。 使用者回報「我留言不見了、重新整理又出現」,沒讀過這章的人會當成靈異事件重啟服務;讀過的人立刻說「這是單調讀破了,把同一個 user 釘到同一台 replica」——病有名字,就有藥方,還能估價。這跟 K8s 排障那張「狀態→病因」對照表是同一種力量:工程能力的很大一部分,是腦中有一本「症狀 → 病名 → 藥方」的字典。DDIA 這章就是複製延遲的那幾頁字典,背下來,值回整本書價。
一致性是菜單,不是開關
這章教我最實用的心態是:一致性不是「開或關」,是一份分級菜單——全要(強一致)最貴,全不要(最終一致)最便宜但怪象叢生,而中間有一排「單點的藥」:自己的資料讀 leader、同一人固定一台、因果放同一分區。多數產品要的不是「全世界即時一致」,是「跟我有關的部分看起來對」——那用兩三帖便宜的藥就夠了,不必為此把整套系統升級成同步複製或分散式共識。這也是我做架構評審時常踩的煞車:有人一遇到延遲怪象就喊「上強一致」,我會先問——你的使用者實際碰到的是三個病裡的哪一個? 對症下那帖藥,成本常常只有十分之一。真正需要強保證的場景留給後面共識那章;在那之前,先記住這句:買一致性跟買保險一樣,買你需要的那幾項,不是全險。