iT邦幫忙

2022 iThome 鐵人賽

DAY 3
7
Software Development

軟體架構師的自我修養系列 第 3

[Day 3] 擴展一個網路服務

  • 分享至 

  • xImage
  •  

昨天,我們提到如何做一場好的設計審閱,其中提到了每場設計審閱中都必須要有設計決策。

今天我們就來看看設計決策會如何影響系統的演化走向。在這篇文章中,我們要談一個系統設計的經典問題,服務的擴充性。之所以經典在於這是每個網路服務的必經過程,也因此能看出決策的重要性。

這邊講的擴充性我們會拆成幾個不同的等級。

  1. 讀取壓力
  2. 寫入壓力
  3. 資料數量
  4. 任務壓力

接下來,讓我們一步一步演化我們的系統。

裸金屬(Bare Metal)

所有的產品都是從一個單機開始的,為了做概念驗證(POC),我們會用最容易實踐的做法:將所有的東西放在一台機器上,而且這台機器通常就是我們手邊的工作機。

使用者透過網頁或者手機app來使用這個基本的服務。這台機器內包含兩個單元:

  1. API用來處理所有的商業邏輯
  2. 資料庫則儲存所有資料

當概念成功驗證,使用者的數量變多時,很容易就會超過工作機的極限。這時,我們會選擇將這一整套系統遷移到專業的伺服器上,這種提升硬體性能的系統升級稱為垂直擴展,或稱向上擴展(scale-up)。

但即便是專業伺服器,依然有硬體極限。除了硬體極限外,同時可能還有預算上限。舉例來說,1TB的SSD硬碟比起512GB的SSD硬碟貴了不止一倍。硬體成本的成長曲線是指數級數的。

因此,有必要將系統元件分離出來以便更好管理。尤其是資料庫,這通常是系統中最貴的元件。

分層式架構

當我們將資料庫和API分離出來,我們可以個別升級每個元件的硬體。從原先的垂直擴展整個系統,演化成垂直擴展單一元件。

資料庫的硬體需求通常是最高階的,但API不需要。其中一個主要原因是,當API的用量增加,通常只需要更好的CPU就能處理更多需求,其餘的硬體規格通常不用提升。

但升級CPU同樣會有硬碟碰到的指數級數的成本曲線。也就是說,比起升級CPU規格,將CPU數量變多會更加划算。因此我們採取增加CPU的策略繼續演化,也就是將API數量變多。

水平擴展

為了增加API數量,我們需要引入一個新的角色來分配進入的流量,稱為流量負載器。流量負載器可以根據不同的演算法來分配流量,最常見的是輪迴(Round Robin)或最少使用(Least Used)。

但我建議使用最簡單的輪迴策略就好,因為使用複雜的演算法也有可能使負載平衡器成為新的瓶頸。

當API用量改變,就可以透過動態改變API的數量來調整擴充性。

到此為止,我們已經解決了用量帶來的API瓶頸,但從圖上可以知道還有另外一個瓶頸,資料庫。

當用量繼續成長,一個資料庫會無法有效地進行處理,進而導致系統的回覆延遲上升。為了解決資料庫瓶頸,我們也想嘗試對資料庫進行水平擴展,但問題來了。API能夠水平擴展是因為無狀態(stateless)的特性,資料庫本身就是有狀態的,因此無法像API一般簡單水平擴展。

讀寫分離

要讓資料庫能夠水平擴展,最常見的做法是讀寫分離。

首先,所有的寫入都在同一個資料庫實體上以維持資料庫的狀態。但是讀取可以分散在讀取副本上,而讀取副本就可以支援水平擴展。

當有任何資料更新,主要的實體會負責將更新複製到每個讀取副本上。透過這樣的方式,就可以根據用量來調整副本數量了。

有幾種常見的資料庫支援這種作法,例如MySQL、MongoDB等。

儘管如此,還是有一個問題。為了要處理更多讀取,我們就需要建立更多讀取副本,這對預算來說有很大挑戰。因為資料庫的硬體規格都很好,所以建立一個副本的成本也很高。

那有沒有辦法既支援更大的流量同時又兼顧成本呢?有,快取。

快取

快取有很多不同的實踐方式,最常見的是以下兩種:

  1. 旁讀(Read-Aside)快取
  2. 內容傳遞網路(CDN)

這兩種方法各有優缺,因此我們會簡單的分析一下兩者。

旁讀快取

不僅是原始的資料庫,我們放一個快取在旁邊。整個讀取的流程如下:

  1. 先從快取讀資料
  2. 若是快取沒有結果,就從資料庫讀
  3. 接著把結果寫回快取

這是最常見的快取情境,根據不同的需求可以設定不同的TTL。

內容傳遞網路

另一種快取實踐是內容傳遞網路。

在負載平衡器的前面建立一個新的元件CDN,當有任何讀取請求時,首先由CDN根據設定來判斷是否直接回應。如果設定命中且CDN內有快取資料,那就直接由CDN回應請求,若否,則繼續往後派送。

當向後派送的請求回應經過CDN時,CDN首先存下來並繼續派給客戶端,如此一來,下次有相同的請求就可以直接回應。

相比於旁讀快取,我們可以發現,CDN的架構更加簡單,而且只需要設定規則就可以生效,不需要改變應用程式的實作。


一個潛在的問題是快取會造成資料不一致,當資料在資料庫被更新時,若沒有更新進快取,那在讀取時就會拿到不一致的結果。

對於旁讀快取來說,當資料庫被更新時,API要主動去將快取內的資料刪除,讓下次的讀取請求可以重新拉取資料。另一方面,CDN得要透過各家廠商定義好的接口執行快取無效化操作,這在實作上比旁讀快取更複雜。

寫入瓶頸

快取和讀寫分離都能有效處理大量的讀取請求,但從他們的架構圖可以發現,這兩個方案都無法解決大量的寫入。為了解決寫入瓶頸,有幾種不同的技術可以套用,其中最常見的兩個如下。

  1. 主主複製(相對於主從複製)
  2. 透過快取寫入

這兩個方案有完全不同的考量,也無法互相比較,因此下面會個別分析兩個方案。

主主複製

雖然圖上看起來每個API都有個對應的資料庫實體,但實際上API並沒有限定只能存取特定的實體,這取決於資料庫的實作和設定。

相較於讀寫分離,你會發現每一個資料庫實體都可讀可寫,寫在某一台實體的資料會同步給其餘實體。

最典型的主主複製範例是Cassandra,他能有效支援大量寫入。另一方面,MySQL也支援主主複製,但MySQL的複製是透過背景工作來執行,這就犧牲了MySQL最重要的能力,一致性。

在考慮主主複製時必須要考慮到PACELC原理。主主複製是能容許分區失效的,因此一致性和可用性無法兼顧。儘管MySQL可以透過外部套件達成具有一致性的主主複製,例如Galera Cluster,但代價是延遲會非常高。

因此,當在選擇主主複製來提升寫入效能時,更重要的是要考慮使用情境是否適合。如果要用MySQL來做,還不如使用其他資料庫,例如Cassandra。

但更換資料的成本很巨大,有沒有什麼方法既可以解決寫入瓶頸,又避免更換資料庫呢?有,再一次,透過快取。

透過快取寫入

在寫入資料進資料庫之前,先把所有資料寫進快取,再由快取來更新資料庫。藉由這種方式,就可以將許多小的寫入轉換成批次處理,以便降低資料庫的負載。

讀取操作則可以像是旁讀快取般,先試著從快取讀。如此一來,也可以取得第一手資訊進而避免旁讀快取時的資料不一致。

透過快取寫入是一個與主主複製完全不同的設計模式,透過架構性的改動來解決寫入瓶頸。儘管應用程式不需要更換資料庫,但依然會產生許多實作複雜度。

我們已經介紹了整個系統如何處理大流量了,從垂直擴展到水平擴展,從API到資料庫。但故事還沒結束,我們還必須面對巨大的資料量。當資料庫的資料量很龐大時,即便只是簡單的讀取,資料庫的效能都會低落,這一樣會帶來效能問題。

資料分片

既然資料量對於單一資料庫來說太大,那麼就將資料均勻地分散在多個資料庫實體上吧。這樣的做法稱為資料分片。

雖然每個叢集內只有一個資料庫實體,但其實也可以是讀寫分離或主主複製。此外,在圖中每個API都對應到某一個特定叢集,但實際上API可以存取每個叢集。

資料分片是把大資料集分割成小資料集的技術,透過預先定義的索引,例如MongoDB的片鍵或Cassandra的區鍵,資料會分布在對應的資料庫實體上。因此,應用程式要存取特定的資料就必須存取特定的資料庫實體。

如果資料分散得夠均勻,那麼每一個資料庫實體上的資料量就會是1/NN是叢集數量。

儘管如此,如果資料過度集中在某個資料庫實體,這表示做資料分片沒有意義。這不僅會造成效能低落,也會產生冗余的資料庫。就像之前說的,資料庫的成本很高昂,若是有冗余將會非常浪費。

一但我們克服擴充性和資料量的問題,下一個挑戰是當一個任務太大,導致影響API和資料庫效能時該怎麼辦?答案是,將任務打散。

訊息

當在API上運行的任務太大,那麼API的回應時間會被影響,除此之外,還會佔用API和資料庫的資源。為了縮短回應時間,最常見的解法是將原本同步執行的任務非同步化,有時候還會將一個大的任務分隔成數個小任務。

這樣的做法稱為事件驅動架構。

API將任務送進訊息佇列中,而處理者會從訊息佇列拿出來執行。當訊息的數量變多時,處理者的數量也可以進行水平擴展。

結論

這篇文章從多角度去分析如何擴展一個系統,從專案的起點,一個裸金屬開始,逐步水平擴展,緊接著資料庫變成瓶頸。為了解決資料庫瓶頸,也有許多技術實踐可以討論,接著當資料量變大或任務變大時會繼續讓系統演化出資料分片和事件驅動。

但在本篇文章中還有一種擴充性沒有談到,那就是商業需求的擴充性。當系統持續演化,能夠乘載更多的使用者和流量時,就會有更多的商業需求提出。要如何應對持續成長的商業需求?近幾年主流的方案是微服務架構。

但要如何正確設計微服務並在微服務系統上做演化,這依然牽扯許多設計決策,正如我們在這篇文章中不斷考量一般。因此,之後的文章中也會提到微服務相關的主題。讓我們拭目以待。


上一篇
[Day 2] 像個專家般做設計
下一篇
[Day 4] 介紹分散式交易
系列文
軟體架構師的自我修養31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0
微中子
iT邦新手 4 級 ‧ 2022-09-11 21:07:49

一天可以寫出這樣的質量 好讚

0
法蘭克
iT邦新手 4 級 ‧ 2022-09-19 15:25:34

好清楚! 獲益良多

我要留言

立即登入留言