擁有良好的系統設計能力,是一個優秀程序員的必要素質。
當然更重要的是,越來越多的公司在面試中考察系統設計能力,尤其是外企巨頭,如谷歌,亞馬遜,微軟等。這些公司對於社招的軟件工程師往往有這方面的要求。
但是系統設計和算法題不一樣,它考察的是程序員對於複雜系統的理解與設計,更重要的是溝通與思維。
還有一點不一樣的是,每一個系統設計問題都沒有明確唯一的答案,目前也沒有直接的教材。不過,令人高興的是,國外目前有一本關於系統設計的書,詳細講解了系統設計涉及到的方方面面,它用幾個實際例子引導讀者一步步瞭解並熟悉系統設計, 以求讓讀者在實際工作中進行系統設計時有更完善的考慮,並且在未來可能的面試中能從容應對系統設計相關考覈。
我在這本書的前幾頁就讀到了其精髓,在此與大家分享。
--------------------------------------------------------------
系統設計過程中的幾大步驟:
- 需求澄清(Requirments Clarification)
- 系統接口定義 (System Interface Definition)
- 粗略估算 (Back-of-the-envelope Estimation )
- 定義數據模型 (Defining Data Model)
- 高級定義 (High Level Design)
- 細節設計(Detailed Design)
- 識別並解決瓶頸(Indentitying and Resovling Bottlenecks)
---------------------------------------------------------------
每一個步驟都介紹了我們具體需要做的事情,下面再稍微詳細地介紹。
第一步:需求澄清
需求澄清可以說是最重要的一步。在我們進行系統設計前,我們必須清楚地明白,要解決的問題是什麼?系統設計問題往往是開放性問題,它沒有唯一的答案,對於求職者來說,在需求澄清這一步的表現對面試的結果是關鍵的。對於一場關於系統設計的面試來說,往往只有40分鐘左右。因此我們必須明確,要關注的重點在哪裏?
舉一個實際的例子,假設要求設計一個類似推特的系統。在進行下一步之前,我們需要問出以下問題:
- 這個服務的用戶能發推特以及關注別人嗎?
- 我們應該設計和展示用戶的時間線嗎?
- 推特包含圖片和視頻嗎?
- 只設計後端?還是前端後端都需要設計?
- 用戶能搜索推特嗎?
- 需要展示熱點話題嗎?
- 如果有新推特,需要彈出消息嗎?
諸如此類的問題,將幫助我們定義出一個合適的系統。
第二步:系統接口定義
這個過程,我們需要定義這個系統用得着的API。這些API可以精確體現出我們希望這個系統能做什麼,確保我們在設計時對需求的理解沒有出現錯誤。
還是用這個推特系統來舉例。 一些可能的API如下:
postTweet(user_id, tweet_data, user_location, tweet_location, timeline, …);
generateTimeline(user_id, current_time, …);
followPeople(user_id, followee_id, …);
markFavorateTweet(user_id, tweet_id, timeStamp, …);
第三步:粗略估算
在我們即將設計系統時,一個很好的做法是,先估算一下它的規模。這將有助於我們之後關注scaling, partitioning, load balancing & caching.
- 系統的預期規模是多大?(比如新推特的數量,推特的瀏覽量,每秒產生的時間線數量,等等)
- 系統需要多少內存?如果用戶可以推圖片和視頻,那需要的內存就完全不一樣了。
- 系統需要多大的帶寬?這對於我們處理網絡擁擠和服務器負載來說非常關鍵。
第四步:定義數據模型
儘早定義數據模型可以理順系統中的數據是如何在各個模塊中流動的。這會指導之後的數據分區和管理設計。面試者應該能識別設計出系統所需要的各個實體,以及它們之間應該怎麼互動,還有諸如數據管理(存儲,分區,加密)等方面的內容。
以這個類似推特的系統舉例:
User: UserID, Name, Email, DoB, CreationData, LastLogin, ect.
Tweet: TweetID, Content, TweetLocation, NumberOfLikes, TimeStamp, ect.
UserFollowo: UserID1, UserID2
FavoriteTweet: UserID, TweetID, TimeStamp
基於數據模型,我們應該選擇合適的數據系統。該用NOSQL還是MYSQL呢?爲了存儲圖片和視頻,我們應該選擇什麼塊存儲方式呢?
第五步:高級設計
畫出系統核心模塊的類圖。我們需要識別出端到端的各個模塊。
對於推特來說,我們需要不同的應用服務來完成讀/寫請求和負載均衡等。如果我們能假設將有更多的讀擁堵(相較於寫),我們就可以決定在不同的場景中使用不同的服務器。我們需要高效的數據庫來存儲所有的推特和支持超大量的讀要求,還需要一個分佈式存儲系統來保存圖像和視頻。
第六步:細節設計
深入挖掘兩到三個模塊,由面試官的反饋來引導我們來決定哪些個模塊需要更多更深入的討論。我們應該有能力提出不同的方案,並且羅列它們的優勢和劣勢,並解釋爲什麼我們選擇其中的某一種,而不是另一種。
記住,這裏沒有唯一的答案。重要的是,在系統現有限制的情況下,對不同的方案進行tradeoff選擇。
你需要對以下問題有所考慮:
- 由於我們要存儲大量的數據,那麼我們如何將這些數據分區到不同的數據庫呢?我們應該把同一個用戶的所有數據都放到一個數據庫嗎?這樣做會造成什麼樣的問題呢?
- 如何處理活躍用戶呢?比如大量發推特的用戶,或者大量關注別人的用戶。
- 既然用戶的時間線是包含最近發的推特,那麼我們在存儲數據的時候要考慮到如何優化最近推特的瀏覽嗎?
- 我們在引入緩存來加速的時候,需要引入到什麼程度呢,在哪個層面引入呢?
- 什麼模塊需要更好的負載均衡呢?
第七步:識別和解決瓶頸
儘可能多地找到瓶頸(存在的或者潛在的),以及儘可能多地提出解決方案。
- 系統中有可能失效的某個點呢?我們如何去消除它?
- 系統對數據的備份足夠嗎?如果某個數據庫或者服務器失效了,我們如何避免對用戶的影響?
- 我們是否有足夠的服務備份,當某些服務失效的時候,不會導致整個系統的崩潰?
- 如何監控系統的服務表現?當關鍵模塊失效或者性能大幅下降的時候,系統是否有警示?
簡單總結一下, 做好準備和有條理,這兩點是成功完成系統設計面試的關鍵。在進行系統設計時,以上幾個步驟基本覆蓋了所有涉及的方面。那麼之後再遇到系統設計時,我們可以沿着這個指導步驟來入手,進行具體的設計。