系統設計五步指南
系統設計對話可能非常具有挑戰性。可能有很多模棱兩可的地方、選項和想法——加上有限的時間和難以解決的問題。根據經驗,我發現了一種通用方法,可以幫助使這些對話更有條理、更有趣、更有成效。
- 要求和目標
系統設計討論通常從要解決的開放式問題開始。
假設需求是:“讓我們設計 Twitter”。
從一開始,每個人都需要保持在同一頁面上。你可以通過澄清問題然後制定討論議程來實現這一點。
該議程的第一步將討論需求和目標,它們基本上可以分爲兩部分:功能性需求和非功能性需求。
功能需求:
功能需求是關於全局的,例如:
-
我們在建造什麼?
-
主要用例是什麼(發佈推文、關注用戶等)?
-
目標平臺是什麼(桌面、移動?)。
-
這方面的工程團隊是什麼樣的?
非功能性需求是關於技術細節的:
-
我們應該如何考慮可用性、延遲和一致性示例:也許我們希望應用程序具有高可用性、低延遲、一致性可能會受到影響。在分佈式系統中擴展時,CAP 定理(一致性、可用性、分區容限)成爲一個問題。
-
我們希望盡最大努力在數據庫中遵循 ACID 模型:原子性(在事務方面全有或全無)、一致性(僅將有效數據寫入數據庫)、隔離性(同時多個事務不會相互干擾)其他),耐久性(即使節點發生故障也不會丟失數據)。
-
存儲和網絡容量是多少?也許我們首先想考慮要關注的關鍵指標,可能是註冊用戶數以及每日或每月活躍用戶數。從這些用戶數中,我們可以假設用戶總數中的一部分實際上是活躍在平臺上的,並且會執行諸如發佈推文之類的操作。此時,您可以使用單位 TB、GB、MB、KB 和 B 來估計數字,然後是相應的時間框架。例如,您可以說每日存儲爲 “每天 1 GB”,大約爲 10 億字節。也許您希望這個數字是“每秒” 而不是“每天”。爲此,一天有 86400 秒,您可以將該天數除以 86400 以獲得每秒存儲量(10 億字節除以 86400 秒大約是 10,每秒 000 字節,與 10 KB 相同)。在您繼續討論時,無論從中得出什麼數字,都將作爲有用的參考。
知道根據討論的性質,有時在這一點上討論非功能性需求並不是優先事項,這很有幫助。無論如何,討論功能需求是必不可少的。
一旦您就需求和目標達成共識,現在是概述用戶流程並深入研究系統 API 的好時機。
- 用戶流程和系統 API
這是您考慮用戶使用產品的所有方式的部分,以及您希望用戶最終採取的路徑。
這是一個用戶流程示例:用戶登錄、進入搜索、獲取結果頁面、可以聯繫業務、填寫表單、發送消息、在消息框中查看消息……
通過這個用戶流程大綱,您可以開始想象需要在幕後發生的不同請求。這直接導致概述 API 調用。
API 調用具有以下元素:
-
採取的行動(例如,編輯用戶資料的 POST 請求,獲取用戶資料信息的 GET 請求)
-
請求所需的參數(例如,您需要提供用戶 ID 來編輯用戶的個人資料)
-
返回的結果(例如,用戶配置文件的用戶數據)
您可以開始編寫 API 調用來涵蓋流程中的每個步驟,這些調用如下所示:
-
POST tweet (unique_key, tweet_data, location) => 返回新推文的 URL
-
GET tweet (unique_key, tweet_id) => 以 JSON 格式返回關於 tweet 的信息
-
…
我們有用戶流程和系統 API 來幫助指導我們的系統設計。現在我們可以開始思考一個圖表,瞭解一切在幕後是如何運作的。
- 高級系統設計
以下是一些典型的設計注意事項:
-
客戶端訪問域,向 DNS 請求獲取 IP 地址。
-
客戶端請求轉到 Web 服務器,但首先進行負載平衡。
-
負載均衡器可以通過服務器數量來修改來選擇服務器,或者獲取來自服務器的流量來確定選擇哪一個(典型的方法包括輪詢、按數量修改、輪詢)。
-
Web 服務器連接到數據庫或文件存儲,但緩存介於兩者之間。
-
Web 服務器還連接到微服務,如身份驗證服務等。
-
緩存保存靜態資源和 API 請求返回的數據,可以使用 write-through / write-back / 等以及最近最少使用或最不常用的驅逐策略。想想 Redis 和 Memcached。
-
數據庫可以是 SQL(關係型,PostgreSQL)或 NoSQL(非關係型,Amazon DynamoDB)。SQL 數據庫非常適合連接等數據庫操作,但在跨多臺機器拆分時不能很好地擴展。NoSQL 非常適合分佈式系統,但不能執行連接等數據庫操作——例如鍵值存儲、寬列表存儲、圖形結構等幾種類型。
-
文件存儲就像 Amazon S3,非常適合照片、視頻、資產。
-
CDN 非常適合緩存靜態資源,並且可以在地理上位於特定位置並保存在該位置特別重要的數據。
當擁有多個數據庫時,有幾種方法可以處理規模。以下是垂直拆分的方法:
-
給機器更多的功率、內存等。
-
在數據庫中,可以使用索引,它基本上是將行分組在一起,這有助於搜索行。
水平拆分
-
可以使用姓氏、位置、哈希函數等對數據庫進行分片。
-
使用多個分片主表,可以在主 / 從表關係中創建每個只讀的複製(我沒有編造這個術語)。寫入到主表,讀取到從表。處理哪些記錄到哪個服務器也可以使用一致的散列來處理。一致的哈希有助於解決一個節點可能會變得超級不平衡的問題,但是當然,這會帶來更多的複雜性。
-
使用負載平衡器或更改負載平衡策略(按數量、循環、最少連接)。
這是一個簡單的圖表示例:
這是一個更復雜的圖表示例,與上面的項目符號更加一致:
有了一個漂亮的圖表,您現在可以更深入地瞭解數據庫方面並寫出數據庫模式。
- 數據庫模式
要編寫表結構模式,您應該考慮數據庫表,然後考慮每個表中的內容。例如,對於 Twitter,我們可能需要 Tweet、User 等表,每個表都有自己的主鍵(粗體)和具有自己數據類型的列:
-
推文:tweetID:字符串,用戶 ID:字符串,緯度:字符串,經度:字符串,createDateTime:字符串,numOfLikes:整數
-
用戶:用戶 ID:字符串,名稱:字符串,電子郵件:字符串,dateOfBirth:字符串,lastLoginDateTime:字符串
-
用戶關注:用戶關注:用戶 ID,用戶關注:用戶 ID
-
喜歡:tweetID:字符串,用戶 ID:字符串
-
…
系統設計對話可以以多種方式進行,不僅在最後,而且在整個過程中。這就是高級主題可以發揮作用的地方。
- 進階話題
您最終可能會更深入地研究分片、緩存、隊列、安全等。以下是一些可能進入討論的示例:
-
安全性:我們如何爲此添加安全性?我們可以加密、淨化用戶輸入以防止 SQL 注入攻擊,使用參數化查詢,並使用最小權限原則。
-
隊列:我們如何使用隊列來幫助處理大量請求?Web 服務器將一些事件 / 消息添加到隊列中,某些工作人員會監聽隊列中的事件(RabbitMQ 是一個流行的工具)。Web 服務器 => 交換 => 帶有主題的隊列 => 工作人員從隊列中消費。您可以擴展隊列數量和工作人員數量。
-
…
總的來說,我希望這是一個對系統設計討論有用的指南!這無論如何都不全面,因爲系統設計可能會變得非常複雜和具體。相反,這涉及關鍵對話點和基本設計概念。
出處:https://www.jdon.com/61714
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/foXquClmYNJCrkUNu6SoXQ