如何一步一步設計一個大規模復雜的系統
良好的系統設計能力,是一個優秀程序員的必要素質,反應出了處理復雜問題的能力,也是面試過程中能否獲得相應的職位和薪酬的關鍵。
最近在 https://www.educative.io/ 上看到一份介紹系統設計的教程:Grokking the System Design Interview[1],里面有很多系統設計實例,如 Dropbox, Twitter, Facebook Messenger, Uber 等,教程是收費的,質量很高,學習系統設計的絕佳資料,該教程的中文資料很少,這里就將其中的核心內容翻成中文與大家分享,如果需看英文原版,回復「系統設計」即可獲取。
許多軟件工程師在系統設計面試(以下簡稱 SDI)時遇到困難,主要有三個方面原因:
- SDI 具有非結構化性質,往往要求開放式設計,很多問題沒有標準答案。
- 他們缺乏開發大型系統的經驗。
- 他們沒有為 SDI 做準備。
像編碼面試一樣,沒有認真準備 SDI 的應聘者,大部分表現不佳,尤其是在 Google,Facebook,Amazon,Microsoft 等頂尖公司的面試,如果表現不及平均水平的候選人,獲得獲得 offer 的機會非常渺茫。另一方面,良好的面試表現總是會帶來更好的回報,或者是更高的職位,或者是更高的薪水,因為這顯示了候選人處理復雜系統的能力。
接下來,我們將按以下步驟循序漸進地解決多個設計問題:
第一步:需求澄清
在需求范圍內提出一些問題有助于澄清需求。設計問題大多是開放性的,并且沒有一個標準答案,這就是為什么要澄清一些具體需求。花費足夠時間來定義系統最終目標有助于在面試中獲得成功。另外,由于系統設計的面試只有 35-40 分鐘的時間,我們應該弄清楚哪些部分需要重點關注。
以設計一個類 Twitter 的服務為例,在開始設計之前應先回答以下問題:
- 我們服務的用戶能否發布推文并關注其他人?
- 我們是否還應該設計來創建和顯示用戶的時間軸?
- 推文中是否包含照片和視頻?
- 我們是僅專注于后端還是前端?
- 用戶將能夠搜索推文嗎?
- 我們需要顯示熱門話題嗎?
- 是否有關于新(或重要)推文的推送通知?
這些問題將決定最終設計的系統長什么樣。
第二步:系統接口定義
定義系統期望的接口(API)不僅可以幫助建立預期的接口協議 ,也可以確保我們沒有弄錯需求。比如類似 Twitter 的服務的接口可能是這樣的:
- postTweet(user_id, tweet_data, tweet_location, user_location, timestamp, …)
- generateTimeline(user_id, current_time, user_location, …)
- markTweetFavorite(user_id, tweet_id, timestamp, …)
第三步:資源預估
預估我們要設計的系統的規模是非常必要的,有助于我們后續的系統擴展、分區、負載平衡和緩存的設計。
- 系統預期的規模,例如,新推文的數量,推文的閱讀量,每秒產生的時間線?
- 我們需要多少存儲空間?如果用戶可以拍攝照片和視頻,又需要多少存儲空間。
- 我們期望多大的帶寬?這對于決定我們如何管理流量和平衡服務器之間的負載。
第四步:設計數據模型
早一點定義數據模型可以弄明白數據如何在不同組件之間進行流轉。數據模型將指導數據分區和管理。設計者應該識別系統的各個實體,它們之間的交互方式以及 數據管理的各個方面,例如存儲、傳輸、加密等。以下是我們的類 Twitter 服務的一些實體:
- User:UserID, Name, Email, DoB, CreationData, LastLogin 等。
- Tweet:TweetID,Content,,TweetLocation,NumberOfLikes,TimeStamp等。
- UserFollowo:UserdID1,UserID2
- FavoriteTweets: UserID, TweetID, TimeStamp
我們應該使用哪個數據庫系統?像 Cassandra 這樣的 NoSQL 是否最適合我們的需求,還是應該使用類似于 MySQL 的解決方案?我們應該使用哪種塊存儲來存儲照片和視頻?
第五步:高級設計
畫一個帶有 5-6 個方框的圖,代表我們系統的核心組件。我們應該識別出足夠的組件來解決端到端的問題,對于 Twitter 而言,我們將需要多個應用服務器來服務所有的讀/寫服務,并配置負載平衡器進行流量分配。假如讀流量大于寫流量,我們可以使用單獨的服務器進行處理這些情況,比如分配 10 臺服務器服務讀請求,2 臺服務器服務寫請求。在后端,我們需要一個高性能的數據庫,該數據庫可以存儲所有推文并支持大量讀取。我們還需要一個分布式文件存儲系統來存儲照片和視頻。
第六步:詳細設計
深入挖掘兩個或三個組成部分;面試官的反饋意見引導我們進一步討論。我們應該能夠提出不同的方法,它們的優點和缺點,并說明為什么我們會選擇另一種方法。請記住,沒有標準答案,唯一重要的是有限資源前提下怎么做出權衡。
- 由于我們將存儲大量數據,因此如何將數據分區到分發到多個數據庫?是否應該嘗試將用戶的所有數據存儲在同一數據庫?它會導致什么問題?
- 如何處理發大量推文或關注很多人的熱門用戶?
- 由于用戶的時間軸將包含最新推文,為了獲取最新推文是否需要優化數據的存取方式?
- 我們應該在多少層引入緩存以加快處理速度?
- 哪些組件需要更好的負載平衡?
第七步:找出并解決瓶頸
找出盡可能多的瓶頸問題,并提出緩解這些瓶頸的不同方法。比如:
- 我們的系統中是存在單點故障?應該采取什么措施緩解這種情況?
- 我們是否有足夠的數據備份,在多少臺服務器宕機的情況下仍可以為用戶提供服務?
- 類似的,我們是否有足夠數量的不同服務在運行,即使一些服務有故障也不會會導致系統崩潰?
- 我們如何監控我們的服務性能?關鍵時刻比如組件發生故障或性能下降時會收到報警嗎?
最后的話
簡而言之,面試前有足夠的準備是系統設計面試成功的關鍵,上述步驟可以指導我們設計一個復雜的大規模系統,涵蓋了的不同方面的面試問題,后續的面試問題,可以參考以上步驟來思考和回答。
參考資料
[1]Grokking the System Design Interview: https://www.educative.io/courses/grokking-the-system-design-interview
本文轉載自微信公眾號「Python七號」,可以通過以下二維碼關注。轉載本文請聯系Python七號公眾號。




























