少於 1 分鐘閱讀

Scaling

有兩種

  • 水平擴張(horizontal): 串連多台伺服器來進行擴張
  • 垂直擴張(vertical): 針對伺服器添加更多資源

垂直擴張相對簡單,不過系統面試通常不在乎垂直擴張,系統面試在乎的是水平擴張,也就是透過串連多台伺服器來進行擴張。

雖然說大多數面試官所想要考驗的是面試者對於水平擴張的理解,不過如果你能計算出具體數字來證實垂直擴張就能夠處理的話,回答垂直擴張就會是更好的選擇,因為 scale out 所需要考慮的事情,像是工作分配、資料一致性、狀態分享等等都很困難,一個常見的誤區是對於任何效能問題不考慮需不需要都直接選擇水平擴張來解決,並且沒有考慮到水平擴張對系統的影響。

Work distribution

工作如何正確、平均的分配流量是分布式系統的一個大難題,通常會使用 load balancer 來滿足這個需求,對於非同步的工作則通常會用 message queue 來實現。

我們在使用這類 work distribution 相關的技術跟工具時,想要解決的關鍵問題是如何確保工作平均地分流,畢竟如果你水平擴張,但工作仍然都由特定的節點來完成的話,那就沒有意義。

Data Distribution

除了工作分配以外,還有就是資料,資料要怎麼同步、儲存在哪

有些人會把資料儲存在記憶體,但資料會隨著服務關閉而跟著消失

有些則儲存在 DB 然後共享給所有的節點,這方式相對簡單,但隨著需求增加,擴張時容易遇到單點故障、併發的難題

DB 也透過分區(partition)來劃分節點可使用的資料,讓節點可以不必與其他節點溝通,減少延遲跟依賴,如果你的系統跟地理區域有關,一個好的作法是使用類似於 REGION_ID 的 key 來作為 partition 的依據

也可以透過溝通來得到所需的資料(也稱 fan-out),但要保持相互溝通的節點盡可能少,以避免任一節點故障、延遲導致的連鎖反應

對資料來說,水平擴張帶來最大的難題是資料同步的挑戰,你有以下選擇

  1. 讀寫一個通過網路躍點(當封包從一個網路轉到另一個網路時,這稱為「躍點」。)的共享資料庫(理想情況下 ≈ 1-10ms)
  2. 在每個伺服器上保持多個冗餘副本,這意味著會有競態條件和一致性問題!大多數資料庫系統是為了解決這些問題而設計的(例如,使用事務)。

在其他情況下,你可能需要使用分散式鎖。無論如何,你都需要準備討論如何保持資料的一致性。

Consistency

一致性,是數一數二重要的問題,從高層次來看,是探討你的用戶可以容忍陳舊資料的程度,一個有著強一致性的系統會確保當資料寫入時,所有後續的讀取都會是最新的資料

弱一致性或常見的最終一致性則反過來,他可以確保在某段時間內會更新(最終會一致),但可能沒有那麼快

選擇何種一致性,就像前面說的,重點是你的用戶可以容忍陳舊資料的程度,對於社群媒體來說,晚一點跟早一點對使用者來說可能沒那麼重要,我們大可選擇最終一致性,但對於銀行系統來說,強一致性就非常重要了

當然,更多時候你的系統可能會部分需要強一致性,部分可以接受最終一致性,就像線上購物,計算商品數量需要確保正確,而商品描述則晚一點更新也沒關係

一致性的概念適用於設計的每一層。即使你使用的是一個強一致性的資料庫,如果你插入了一個快取並使用 TTL(存活時間)來維護資料,通過該快取進行的讀取將會是最終一致性的。

Locking

有些資源我們必須互斥,也就是確保同一時間只能有一個人在修改跟讀取,像是商品數量,如果已經沒有庫存但卻沒有互斥導致有人讀取到錯誤資訊,那可能就會導致錯誤下單,對客戶的體驗是大扣分的

鎖會出現在系統的各個層級,OS Kernel、App、DB、分佈鎖等等到處都有,這對於正確性的確保是必須的,但累積下來對效能來說可能會有重大的影響。

有鎖的地方就有 race condition,我們需要考慮以下:

  • 鎖的顆粒度(Granularity of the lock) 我們希望鎖能夠越精確越好,能夠鎖在真正需要的地方,盡可能不要影響其他東西,你不會希望使用者更換名稱然後將整張 table 鎖住導致其他使用者都登入不了
  • 鎖的持續時間(Duration of the lock) 鎖的持續時間越短越好,這代表我們使用鎖時要留意互斥的邏輯是否很耗時以及鎖的時機點是否剛好,你可能會想要使用者更換名稱時簡單上個鎖,但不用整個 request 都上鎖
  • 是否可以不用鎖 在很多時候,我們可以不用那麼“悲觀”,可以使用“樂觀鎖”,特別是遇到 read-only 或是可以 retry 的工作,在樂觀鎖的情況下,我們可以假設不用鎖也可以完成工作,事後檢查正確與否就好,在大多數系統中,我們可以使用 ”compare and swap“ 來達成

    樂觀鎖建立在我們覺得大多數的情況下是“沒有衝突的”,當然,很多系統可以“樂觀”,但反過來說,有些系統你必須“悲觀”,像是銀行帳戶,你可不能犯錯,就算真的沒有人會同時寫入,你也必須謹慎一點乖乖上鎖

    補充:樂觀鎖的事後檢查機制通常是透過一個數值來代表 version,並且在每次更新時確認寫入後的版本是否跟寫入前獲得的數值 + 更新條件(可能加上 1 之類的)一致,如果不一致,代表中間有人有操作,我們可以重新嘗試,反之則天下太平,我們可以直接修改