DDIA 導讀 CH 6 — 複寫 (上)
2026年10月12日
在《Designing Data-Intensive Applications》的第六章中,作者切入談「資料複寫 Replication」這個資料密集型應用系統中,非常核心的概念。資料複寫是指,一份資料不只放在一台機器上,而是讓好幾台機器同時保有相同資料的副本。
直觀上來看,同樣的資料複寫到多台機器上,這會佔據更多的空間;在實務上,會需要多花錢買機器,或者要多租機器來用,會是額外的成本。既然如此,為什麼要做複寫? 好處在哪裡? 此外,在實務上執行複寫的困難點在哪? 不同複寫方式的技術取捨又為何? 作者都在這個章節逐一討論。
為什麼需要複寫?
雖然說複寫會帶來成本,但對於系統來說也有不少好處。在書中作者提到三個主要的好處。
第一個是透過複寫,讓資料離使用者更近,藉此讓讀取速度加快。舉例來說,如果使用者在台灣,但資料庫在美國,每次使用者瀏覽網頁,請求要橫跨太平洋到美國的資料中心再回傳回來,中間的距離會帶來額外的延遲。
這類延遲可以透過在距離使用者較近的地區部署讀取副本來降低。只要某些讀取請求可以由當地副本處理,台灣使用者就不必每次都跨越太平洋存取美國的資料中心。不過在單主節點架構中,寫入通常仍須送往主節點,因此地理副本主要改善的是可由副本承接的讀取延遲。
第二個是提高系統可用性。如果資料全都放在一台機器,當那台機器故障,很可能導致服務不可用。但如果有多台副本,其中一台壞掉,另外的機器能夠頂替,讓系統持續可用。換句話說,複寫能夠降低單點故障的問題。
第三個是能增加讀取吞吐量。不少系統是讀多寫少 (例如多數人用電商網站瀏覽多過下單,逛社群媒體讀貼文多於發文)。這時如果所有讀取都由單一台機器處理,能支應的流量終究會遇到單台機器能到達的上限。如果有多個副本,能把請求分散到不同機器,這樣每台機器分擔部分流量,整體的讀取吞吐量就能獲得提升。
單主節點複寫(single-leader replication)
在談複寫時,不可避免地要思考「如何確保所有資料,最後都會出現在所有副本上」這個問題。由於每次資料庫有寫入,每個副本都需要更新,不然就會有不一致的狀況。要避免這種問題,單主節點複寫是最常見的解決方法。
顧名思義,單主節點複寫是有一個主要機器負責接受寫入,其他機器跟著同步。在這個模式下,會有兩個角色,一個是領導者 (主資料庫,或稱主節點),另一個是追隨者 (副本資料庫,或稱從節點)。所有客戶端寫入都會送到主節點,再由主節點將變更傳遞給副本。副本不接受客戶端直接寫入;至於是否承接讀取,則依資料庫的能力與系統設定而定。
!image.png
當只有一個主資料庫,搭配多個副本資料庫,寫入的入口會比較明確,整體維護起來也比較簡單。因此,在許多資料庫 (包括 PostgreSQL、MySQL、MongoDB 等等),都會選擇用單主節點的複寫當作預設複寫方式。
同步與非同步的取捨
雖然說單主節點複寫是相對簡單的架構,但在實務上還是有許多技術難題要克服。
其中第一個要克服的點是定義「什麼叫寫入成功」。舉例來說,當使用者在社群媒體網站上更新大頭貼,寫入請求發到主資料庫;主資料庫收到後,把新大頭貼資料存下來後,廣播到各個副本資料庫。這時主資料庫要直接回傳成功,還是等副本資料庫回傳確認收到,才回傳成功?
在下圖我們可以看到幾個時間軸,一種選擇是主資料庫完成資料變更後,直接回傳 ok ,所有副本的更新都是非同步。另一種是只要一個副本資料庫 (例如 副本 1 ) 完成更新後就回傳 ok,這是屬於同步與非同步混合 (副本 1 是同步,其他則是非同步)。最後一種,則是要等到所有副本都更新完才回傳 ok,也就是全部都同步。
!image.png
大家覺得這三種不同方式,分別要面臨什麼取捨呢?
同步複寫可以降低已確認寫入在主節點故障後遺失的風險。依照設定不同,系統可能保證至少一個、指定數量,或所有同步副本已收到並持久化該筆變更。對於資料安全特別看重的系統,例如金融與庫存類的資料,這種做法能帶來更多的安心。但同步顯而易見的問題,是回傳給使用者 ok 的時間會比較晚一點。此外,如果某台副本資料庫出問題,主資料庫要等副本回傳 ok,可能會要等更久,這種卡住的情況對使用者體驗來說很不理想。
非同步則是反過來,主資料庫可以非常快就直接回傳 ok 給客戶端,使用體驗比較好一點。但反過來說,因為沒有確保副本資料庫有成功更新,存在比較大的資料丟失風險。在一些重視低延遲,且可以接受部分資料丟失的情境,這種做法經常被用到,例如一些事件分析的 log 系統,或者是瀏覽記錄追蹤的系統,丟掉一小段資料不一定會造成事故。
另外,同步與非同步混搭的方式,則是比較平衡的做法。例如只要確保一台副本資料庫有回傳 ok,就直接回傳給使用者 ok。這樣能確保至少另一個節點已保存該筆變更,同時不用等到所有副本都回傳 ok,延遲會比較低一點。
在實務上,會根據系統的特性來選擇。有時候同步與非同步的混搭,會是多數副本有同步 (例如總共有 5 台副本,其中 3 台更新成功) 時,就回傳 ok。要怎麼選沒有標準答案,更多是需要針對系統比較需要資料安全,還是更看重低延遲來決策。
新增副本的困難點
單主節點複寫除了要面臨選擇同步或非同步複寫這個問題,還需要面對新增副本的困難。
想要加入新副本,不是新增一台機器,然後把主資料庫的資料都倒過去這麼簡單,因為多數的主資料庫不是靜止不變的。客戶端會不斷向主資料庫寫入新資料,這就像是去影印店要複印一本書,原本書本有十個章節,結果複印完後發現又新增兩個章節,這樣複印的結果就會不完整。
面對這問題,直觀的解法是把主資料庫鎖住暫時不接受新的寫入,但這樣會讓系統的可用性降低 (事實上,目前還是有許多系統是採用這種做法,在建立初始副本時暫停寫入,以換取較簡單且容易驗證的一致性流程;代價則是服務在這段期間內無法正常接受寫入)。
如果想要在不停機的狀況下完成這件事,書中提到可以透過取得主資料庫的「快照」的方式來做到。具體來說,會把快照複製到新的副本資料庫,讓副本資料庫獲得某個時間點下,主資料庫的狀態。接著再比對快照時間點後有的所有變更 (在 PostgreSQL 可以透過日誌序列號,在 MySQL 則可以透過 binlog 或 GTID 來完成),找出變更後補上快照建立後尚未套用的變更,這時就能夠完整補上變更進度。
副本資料庫的成本
在開頭有提到,如果想要有多個副本會增加維運的成本。以現代的雲端架構來說,如果要租用高效能磁碟或記憶體,費用都不算小。如果資料庫中有很多資料其實不常被用到,在多副本的架構下使用貴的磁碟與記憶體,就像租用黃金地段的辦公室來放紙箱一樣,從成本角度看不太合理。
因此,在業界逐漸出現搭配物件儲存服務 (例如 AWS 的 S3 或者 Google Cloud Storage) 的做法。這類服務比起雲端資料庫便宜許多,且提供很好的持久性與跨區複寫能力 (例如用 S3 能很簡單地把資料做自動跨多區複製)。
雖然說物件儲存服務有便宜又方便的特性,但比起磁碟來說,延遲性高很多 (換句話說,慢很多)。對此,在實務上,選擇用物件儲存服務時,會把架構拆成多層架構;經常會被用的資料會放在磁碟或記憶體,比較少用的則放在物件儲存中。
近期還有一個新的概念叫零磁碟架構,具體來說不把本機磁碟當作持久化的方案,真的需要持久的資料都存在物件儲存中,本機磁碟或記憶體只當成快取。這樣的好處是主資料庫節點會變成接近無狀態,如果壞掉了也不擔心資料丟失,因為持久化的資料還存在物件儲存中。當然,這麼做還是有成本,因為如果哪天主資料庫掛掉,回頭去跟物件儲存拿資料時會遇到較高的存取延遲。
後半篇將進一步討論「複寫時的延遲問題該如何處理?」。當副本還沒跟上主節點的更新時,使用者可能讀到舊資料,應用程式該如何應對?這部分內容僅供 E+ 會員 閱讀。
加入 E+ 會員方案
如果你覺得這篇分享有幫助,想閱讀更深入的內容,歡迎加入 E+ 會員。除了有會員專屬的深度文與影片,探討前後端開發、AI 工程、職涯發展,同時有 Discord 社群一同交流與成長。
許多 E+ 會員透過所任職公司的教育訓練補助 (E+ 會開立有統編的發票),不用花自己的錢,也能每週透過深度主題文與社群交流,拓展技術視野、加速職涯成長。