1. 程式人生 > >系統設計面試題 之 如何設計一個能擴充套件到百萬使用者級別的系統

系統設計面試題 之 如何設計一個能擴充套件到百萬使用者級別的系統

本文翻譯自https://github.com/donnemartin/system-design-primer/blob/master/solutions/system_design/scaling_aws/README.md 因為原文有很多重複內容並且組織比較零散,所以譯文調整了部分原文順序,並整理了很多過於瑣碎的內容。

第一步:用例和約束 在面試過程中我們應該向面試官詢問和討論用例和約束。但是本文由於沒有面試官可以討論,我們會定義一些用例和約束。

1. 用例 本題中我們將處理下面的用例: 1)使用者傳送讀或者寫請求 2)服務要處理並存儲使用者資料,然後返回結果 3)服務需要從一個少量使用者的系統升級至百萬使用者 4)討論當一個系統從少量使用者升級至百萬使用者的過程中的一般擴充套件模式 5)服務有高可用性 解決用例3,4和5需要不斷執行Benchmark/負載測試並且分析瓶頸。

2. 約束和假設 這部分內容大部分省略,太瑣碎。

第二步:總體設計 總體設計包含了所有必要的元件。

第三步:設計核心元件 假設我們只需要服務1-2個使用者,那麼我們就只需要一臺伺服器,這臺伺服器上安裝了資料庫和web伺服器。當我們需要升級的時候只需要做垂直升級就行了。另外,如果我們的資料是關係型資料,那麼可以選用MySQL資料庫。防火牆只打開必要的埠:80,443和22。

第四步 擴充套件我們的設計1.Users+ 1)假設 我們的使用者數量在上升並且伺服器的負載也在增加。我們的測試和分析結果顯示,MySQL資料庫佔用了大量的記憶體和CPU資源,而使用者內容正在不斷佔用磁碟空間。 到目前為止,我們一直採用垂直升級。不幸的是,這種升級方式比較昂貴並且不允許MySQL資料庫和Web伺服器單獨擴充套件。2)目標

我們的目標是減輕伺服器的負載並且允許獨立擴充套件。為此我們可以把靜態內容單獨儲存到一個面向物件儲存伺服器上,並且我們可以把MySQL資料庫移動到一臺單獨的伺服器上。但這麼做的缺點是,系統的複雜度上升了,並且我們需要額外的安全措施來確保新增伺服器的安全。3)把靜態內容分開存放 我們可以考慮使用面向物件儲存例如S3來儲存靜態內容。S3的擴充套件性和可靠性都很高,並且伺服器端是加密的。User files、JS、CSS、Images和Videos都可以移動到S3。4)把MySQL資料庫移動到一臺單獨的伺服器上 5)系統的網路安全 我們需要加密傳輸的資料,並且我們可以考慮使用虛擬私有云。我們可以為Web伺服器分配公有網址,這樣它可以從internet傳送和接收資料;我們要為其他的伺服器分配私有網址,他們不訪問外網;對於每臺伺服器我們只開放列在白名單的IP的埠。

2.Users++ 1)假設 我們的測試結果顯示網站的效能瓶頸在我們的唯一的一臺web伺服器上。在峰值期間這臺伺服器的響應會很慢,並且有時候會無響應。因此我們需要更高的可用性和冗餘。2)目標 我們可能只需要以下的一種或者幾種方法來解決假設中的問題。a.我們可以使用水平擴充套件來處理不斷增加的系統負載和解決單點故障問題。 我們可以增加負載均衡器(例如ELB)。ELB的可用性很高;如果你配置自己的負載均衡器的話,你可以把它們配置為active-active或者active-passive型別;我們可以讓負載均衡器來處理SSL認證,這樣可以降低後端伺服器的負載並簡化認證管理。 我們可以讓web伺服器群分佈於多個地區以提高系統冗餘性。 我們可以讓MySQL伺服器群分佈於多個地區以提高系統冗餘性。b.我們可以把應用伺服器從web伺服器中分離出來; 我們可以單獨地配置web伺服器群和應用伺服器群;web伺服器群和應用伺服器群可以有各自的負載均衡器;我們可以把應用伺服器分為Read API伺服器和Write API伺服器。c.我們可以把靜態內容(和部分動態內容)移動到CDN上以減少系統負載和延遲。

3.Users+++ 1)假設 我們的測試資料顯示,我們的資料庫由於大量的讀請求而效能糟糕。2)目標 我們可能只需要以下的一種或者幾種方法來解決假設中的問題。 a.把以下資料移動到快取伺服器(例如Elasticache)中以降低負載和延遲: 一是訪問頻繁的MySQL資料。不過,請首先嚐試開啟MySQL的快取;如果不能解決效能瓶頸的話,再考慮快取伺服器。 二是Web伺服器的會話資料。這樣web伺服器就變成無狀態的,從而可以自動擴充套件。 b.新增“MySQL讀伺服器(MySQL Read Replicas)”以降低“寫伺服器(write master)”的負載; c.新增更多的web伺服器和應用伺服器以加快響應。3)新增“MySQL讀伺服器(MySQL Read Replicas)” a.“MySQL讀伺服器(MySQL Read Replicas)”可以幫助降低“寫伺服器(write master)”的負載; b.我們需要在Web伺服器上區分“讀”和“寫”的邏輯; c.為“MySQL讀伺服器(MySQL Read Replicas)”伺服器叢集新增負載均衡器; d.大多數的服務是“讀”負載大於“寫”負載的。

4.Users++++ 1)假設 我們的測試資料顯示,我們的網站流量在工作時間會急劇上升,但是下班後會急劇下降。所以,我們可以根據實際負載來自動啟動或者關閉伺服器。2)新增自動擴充套件 a.為web伺服器和應用伺服器分別建立伺服器叢集,叢集中的伺服器應該分佈於多個不同的區域,這樣可以提高可用性。 b.設定叢集伺服器的上限和下限; c.監控各臺伺服器的cpu負載、延時以及網路流量等日誌以出發伺服器的自動開啟和關閉; d.自動擴充套件的缺點是增加了系統的複雜性;另外,開啟或者關閉伺服器會有一定的延遲。

5.Users+++++ 當用戶進一步增加的時候,我們需要通過測試和效能分析找到瓶頸,而非盲目的優化。 a.如果我們的MySQL資料庫持續增長,那麼我們可以考慮只在資料庫中儲存最近一段時期的資料,而把其他時間的資料儲存在資料倉庫中(例如:Redshift)。諸如Redshift之類的資料倉庫可以輕鬆處理每個月新增1TB的資料的情況。 b.我們可以通過擴充套件記憶體快取伺服器來處理平均每秒40000個讀請求的負載。另外,該方法也能很好地處理非均勻分佈的流量和流量峰值問題。注意:SQL“讀伺服器(Read Replicas)”可能會有快取沒有命中的情況,我們可能需要額外的SQL擴充套件方法來解決該問題。 c.當平均每秒有大於等於400寫操作的時候,只有單臺寫伺服器的SQL Master-Slave可能會無法應付,這時候我們需要額外的擴充套件方法。 d.我們也可以考慮把資料移動到NoSql資料庫上以進一步解決大量的讀寫請求。

額外的資料庫擴充套件方法包括: a.Federation b.分片 c.反正規化 d.SQL調優

我們可以進一步劃分應用伺服器以更好地獨立擴充套件。不需要實時完成的請求或者操作可以用Queues和Workers完成。例如,在照片服務中,照片上傳和縮圖生成的功能可以被分開: a.使用者上傳照片 b.應用伺服器將一個job放入Queue c.某臺伺服器上的Worker會把job從Queue中取出,然後建立縮圖,更新資料庫,將縮圖儲存入面向物件儲存。