跳到主要內容
黯羽輕揚每天積累一點點

Scalability_系統設計筆記 1

免費2020-01-05#Mind#Back-End#高可扩展性#High Scalability#CS75 (Summer 2012) Lecture 9 Scalability Harvard Web Development David Malan#横向扩展没有上限#single point of failure

如何理解可擴展性?

一。Scalability 是什麼?

Scalability is the property of a system to handle a growing amount of work by adding resources to the system.

(摘自 Scalability

即,通過向系統添加資源的方式應對不斷增加的工作量

那麼,如何添加資源呢?

二。擴展硬件資源

加資源有兩種加法,縱向擴展與橫向擴展

縱向擴展

縱向擴展(Vertical scaling),即提升單機配置,對單台機器加內存、處理器、硬盤等硬件資源。投入足夠多的預算,就能砸出一台配置豪華的服務器

然而,這種單點強化式的擴展不可能無限進行下去,因為很快會達到頂配(或者耗光預算),所以不算是個完整的解決方案

橫向擴展

另一種加資源的方式是橫向擴展(Horizontal scaling),即加機器,數量上從一台擴展到多台,多服務器形成拓撲結構。投入足夠多的預算,就能擁有一個機房,甚至遍佈全球的數據中心

理論上,橫向擴展是沒有上限的,我們可以使用無限多台服務器支撐無限多的用戶請求。而且,橫向擴展相當於引入了冗餘(Replication),比單機更可靠

但機器由一台變成多台之後,面臨的最大問題是資源分配,如何充分利用這些機器?即,如何均衡負載?

三。負載均衡

負載均衡器(Load Balancer)負責把用戶請求分發到多個服務器上,具體的,公網 Load Balancer 根據路由規則分發入站 HTTP 請求,決定把數據包實際發送給哪個內網服務器

常見的分發策略有:

  • 基於負載情況分發

  • 輪流均分

  • 基於資源依賴情況分發

當然,最理想的分配策略是基於服務器當前負載情況分發,比如把新請求交給不太忙的服務器,但問題在於負載情況不那麼容易精確獲知

而最簡單的分配策略是輪流(Round robin),比如第一次請求 URL 時返回 Server1 的 IP 地址,第二次返回 Server2 的 IP 地址……然而,輪流工作意味著一視同仁,假定每個請求的工作量相同,每個 Server 的處理能力也相同,但實際場景大多不滿足這樣的條件

P.S. 不建議用 DNS 來充當負載均衡器(添加一系列 A 記錄),因為操作系統以及應用層的 DNS 緩存會破壞這種輪流均分的機制

另一方面,不同類型的服務對資源的依賴情況(帶寬、存儲、計算能力等)可能不一樣,所以也可以採用專用服務器,並根據資源依賴情況分發,比如對 gif、jpg、image、video 等使用不同的專用服務器,並通過子域名等方式來區分

會話保持

加一層 Load Balancer 解決了資源分配的問題,但又帶來了一個新問題:前後兩個請求可能被負載均衡器轉發到不同的服務器上,如果這兩個請求有關聯(比如登錄和下單),前置的狀態就會丟失(用戶剛登錄完點擊下單接著可能又要求登錄)

一種解決辦法是粘滯會話(Sticky sessions),把相關聯的請求轉發給同一台服務器:

Send all requests in a user session consistently to the same backend server.

(摘自 Load balancing (computing)

比如在 Cookie 中帶上服務器的標識信息,之後的一系列請求都轉給那台服務器

P.S. 但 Cookie 可能會被禁用,因此一般會綜合使用多種方式來保持會話

另一種方案是把 Session「外包」出去,存放到公共的地方,供其它服務器共享訪問:

Every server contains exactly the same codebase and does not store any user-related data, like sessions or profile pictures, on local disc or memory. Sessions need to be stored in a centralized data store which is accessible to all your application servers.

至此,我們增加了一些機器,並通過一個負載均衡器讓多台機器共同分擔運轉起來了,看起來一切都很完美……那麼,如果這個負載均衡器 down 掉了呢?

四。引入冗餘

引入負載均衡器之後,所有請求都要先經過負載均衡器,負載均衡器就成為了網絡拓撲結構中脆弱的單點,一旦發生故障,身後的所有服務器就都無法訪問了

冗餘負載均衡器

為了避免單點故障(Single Point of Failure),負載均衡器同樣需要引入冗餘(比如使用一對兒負載均衡器),一般有兩種故障轉移(Fail-over)模式:

  • 主動 - 被動(Active-passive):主動的工作,被動的備用,主動的 down 掉後被動的上

  • 主動 - 主動(Active-active):同時工作,一個 down 掉之後不影響

無論採用哪種工作模式,引入冗餘都能縮短宕機時間,提升系統可靠性與可用性

五。擴展數據庫

理論上,有了可靠的負載均衡機制,我們就能將 1 台服務器輕鬆擴展到 n 台,然而,如果這 n 台機器仍然使用同一數據庫的話,很快數據庫就會成為系統的性能瓶頸和可靠性瓶頸

如法炮製,我們可以擴展數據庫的處理能力,多加幾個庫,即引入冗餘,一般有兩種模式:

  • 主從複製:主庫直接讀寫,從庫在主庫收到查詢時,執行相同的查詢。如果主庫 down 掉了,就在從庫裡面提升一個作為主庫

  • 主主複製:都可以寫,寫操作也會被複製到另一個庫中

數據庫引入冗餘之後,甚至還能對多個從庫進行負載均衡(尤其適用於讀密集的場景):

以及按內容特點分區存儲(Partitioning):

將姓名以 A-M 開頭的數據存放到左邊的幾個數據庫,N-Z 開頭的存放到右邊

同時,也可以通過分庫分表(Sharding)、反範式化(Denormalization)、SQL 調優(SQL tuning)等方式優化查詢

到這裡,數據庫層所能做的擴展優化似乎已經達到極限了,那麼,還有其它辦法能夠減輕數據庫的壓力嗎?

六。緩存

另一種思路是盡可能減少數據庫操作,比如在 Web 服務與數據之間增加一層內存緩存,查詢時優先走緩存,緩存中沒有才從數據庫中取

一般有兩種緩存模式:

  • 緩存查詢結果

  • 緩存對象

緩存所有查詢結果最大的問題在於,數據發生變化後,很難判定緩存是否過期

It is hard to delete a cached result when you cache a complex query (who has not?). When one piece of data changes (for example a table cell) you need to delete all cached queries who may include that table cell.

而緩存對象是指緩存根據原始數據組裝出的數據模型(比如一個 Java 類實例),優勢在於獲知數據變化之後,能夠丟棄與之具有邏輯關聯的數據對象,從而解決緩存過期的難題

至此,我們已經自下而上地討論了包括硬件資源、數據庫、緩存在內的可擴展性問題,那麼,Web 服務自身應該如何擴展?

七。異步處理

對於 Web 服務而言,提升可擴展性的主要途徑是將耗時的同步工作改成異步處理,從而允許將這些工作「外包」給多個 Worker 去做,或者提前完成能夠預知的部分

參考資料

評論

暫無評論,快來發表你的看法吧

提交評論