ExplainThis 全端開發雙週報 #89 什麼是複寫 (Replication)?為什麼資料庫需要複寫?

資料複寫 (replication) 是指同一份資料,不只放一份,而是同時放在多個節點、機器、區域當中。舉例來說,內容傳遞網路 (CDN) 會把內容複製到多個地理位置,藉此提高可用性 (因為一個節點故障,還有其他節點可用),同時降低延遲 (讓資料離使用者更近)。

我們可以把複寫理解成共置的概念再往前一步,共置是指透過把資料放到更接近需求的地方,藉此降低延遲;而複寫則因為代表不同位置有資料的副本,意味著在每個有副本的區域,都能夠從較近的副本中獲得回應,藉此加快回應的速度。

從延遲的角度看常見的資料複寫策略

在實務上,有三種常見的資料複寫策略,分別是單主節點複寫 (single-leader)、多主節點複寫 (multi-leader) 以及無主節點複寫 (leaderless) 這三種策略,以下我們會分別討論這些模式跟延遲的關係。

單主節點複寫是指系統中有個特定節點 (又稱為領導節點 leader),會負責接受客戶端的寫入,其他節點則會從主節點接收更新,讓自己的資料副本能跟上。從架構的角度來看,單主節點複寫相對簡單,但也會有延遲上的問題。

因為寫入只能送到主節點,所以假如今天主節點在美國,亞洲的使用者要寫入資料,就需要一路傳輸到美國;與此同時,在亞洲其他節點的副本資料庫,需要等美國回傳資料才能同步。如果是全球的系統,在這種情況下,會因為物理距離關係導致延遲變高。

多主節點複寫是單主節點複寫的延伸,會有多個領導節點,每個都能接受寫入,然後再彼此同步。通常在單一資料中心的場景下,因為這種架構比較複雜 (例如不同主節點同時收到相衝突的寫入,要如何解衝突),且單一資料中心內的機器物理距離都很近,所以不太會用。但是在多資料中心的場景下,從延遲的角度看,這種做法能帶來很大的改善。

以上面的例子來說,亞洲區的使用者要寫入資料,不用一路送到在美國的主節點,而是可以直接寫入在亞洲區的主節點;同樣地,在美國、歐洲區的寫入,也都可以直接寫到當地的主節點。這種做法能夠免去寫入時的長距離傳輸,進而有效降低延遲。

當然,降低延遲背後不是沒有代價,因為主節點數量變多,就需要處理更複雜的寫入衝突、跨區延遲,以及假如有任何故障後主節點要重新恢復與同步的問題。

無主節點複寫是指所有的節點地位都相同,任何節點都能接受寫入。從延遲的角度來看,這很有吸引力,因為客戶端可以寫到最近可用的節點,不用找特定的主節點。只是因為任何節點都能接受寫入,要維持節點間的一致性,所以也會有額外的實作複雜度。

一致性與延遲取捨

從上面的討論來看,複寫並不難理解。但實務上要做到在不同區域維護多份資料副本,就必須面對「副本如何保持同步」的難題。副本的同步牽涉到一致性問題,也就是如果某筆資料寫入後,會立刻在不同的資料副本中看到嗎? 如果不會馬上看到,那要等多久才看到?

從單主節點到無主節點,都是延遲與一致性之間的取捨。不同程度的一致性會對應到不同的延遲,如果要確保所有副本都同步資料後,才能讓使用者讀取,這樣延遲會提高;但如果想要降低延遲,就必須承擔有些副本還沒被同步到的不一致問題。

在實務上沒有標準答案,需要根據不同的情境,選擇合適的取捨。在不同的情境下,都需要問「是否願意為了更快的寫入與讀取速度,接受讀到過期資料的可能」,或是「是否希望使用者永遠看到最新的資料,即使這意味著讀取資料的速度可能會變慢」。

在上面我們談一致性與延遲的取捨,是用一個很概略的方式討論。在實務上,則更像光譜一樣,從比較強的一致性到相對弱的。其中,最常會聽到的一致性是強一致性 (strong consistency) 與最終一致性 (eventual consistency)。

強一致性承諾即使有很多副本,但對於應用程式來說,只會看起來像有一份資料。每次的操作後,都像是在操作同一份資料,使用者不用知道背後其實有多份資料,應用層也不用擔心是否讀到舊資料。雖然這聽起來很不錯,但代價是延遲會比較高,因為系統需要來回確認副本都更新到了,如果有出問題則要重試,這些往返都可能增加延遲。

最終一致性則不保證在當下所有副本資料都一致,但是承諾最終副本之間的狀態會一致。舉例來說,假如遇到網路斷線,可能會讓某一個副本的更新落後,讓使用者可能看到舊的資料;在最終一致性的狀況下,會接受這種狀態,但是強一致性則不會。

雖然從希望使用者永遠看到最新資料的角度,最終一致性相對不理想;但是從降低延遲的角度來看,最終一致性反而是許多低延遲系統的選擇。最主要的原因,是最終一致性省去昂貴的跨節點協調。比起在寫入時就對多節點做確認,最終一致性可以接受先完成本地節點的寫入,其他節點的同步稍後再說。

舉例來說,如果某個寫入在亞洲區發生,在最終一致性的狀況下,更新完亞洲區的副本就可以回傳成功;但強一致性的狀況下,系統在回傳寫入成功前,必須與其他地區的副本完成必要的協調,會增加跨區網路往返所需的時間。相比之下,對使用者來說,前者的延遲就會低許多。

閱讀更多

如果你覺得這篇分享有幫助,想閱讀更深入的內容,歡迎加入 E+ 會員,閱讀完整版內容 (公開的雙週報是節錄前半段內容)。除了有會員專屬的深度文與影片,探討前後端開發、AI 工程、職涯發展,同時有 Discord 社群一同交流與成長。

許多 E+ 會員透過所任職公司的教育訓練補助 (E+ 會開立有統編的發票),不用花自己的錢,也能每週透過深度主題文與社群交流,拓展技術視野、加速職涯成長。

本期推薦

  • 《How Kubernetes probes work》一文用可互動的模擬介紹三種 Kubernetes 健康檢查,讓你直接調整設定、觀察容器重啟與流量分配的變化 (連結)。從啟動時間設太短造成反覆重啟,到把資料庫故障納入存活檢查而引發連鎖失敗,推薦讀完也動手玩玩
  • 為什麼開發伺服器常用 3000、8080,Vite 卻選了 5173?《The accidental history of 3000, 8080, and other port numbers》一文梳理早期軟體與文件,揭開埠號的歷史 (連結)。推薦對這類工程冷知識感興趣的人一讀
  • 《Hidden Cost of Hydration Mismatches》一文談到一個容易忽略的前端效能問題:React 的伺服器與客戶端渲染結果不一致時,即使畫面看起來沒變,重建 DOM 仍可能讓 LCP 多出數秒 (連結)。推薦正在追查頁面載入效能的人一讀
  • 《Building Gin: Simple Over Easy》一文回顧 Go 網頁框架 Gin 的設計取捨,談到框架如何兼顧上手方便 (連結)。文中用明確的請求流程,說明 Gin 如何減少隱藏機制與執行成本,推薦對框架與 API 設計有興趣的人一讀
  • Claude 團隊在《How we made claude.ai 3x faster in two weeks》中,分享如何讓 Claude 參與,在兩週內將核心使用流程加速約三倍 (連結)。除了具體的前端優化,文中也介紹如何把穩定、可重複量測的指標放進 CI,並確認指標改善確實能縮短使用者等待時間,推薦想把 AI 納入效能優化流程的團隊一讀
  • 大型程式碼差異頁面要保持流暢,光是減少畫面上的 DOM 還不夠;Pierre 在《On Rendering Diffs》中分享同時處理渲染、運算與記憶體成本的經驗 (連結)。推薦正在開發大型列表或程式碼檢視工具的人一讀
  • Etsy 在《Migrating Etsy’s database sharding to Vitess》中,分享如何將超過 425 TB 的 MySQL 分片架構逐步交給 Vitess 管理,解決原本分片索引資料庫的單點故障問題 (連結)。文中也分享跨資料表交易如何打破逐表切換的計畫,相當精彩
添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论