動機
會知道system design primer是因為某場面試,原本以為system design在網路上沒有什麼資料
結果是很多資料,但是system design資料會多,很大一部分是system design包含的面向很廣
所以來整理一下,作為了解的開始
這篇是介紹會用到的工具,接下來是設計 但設計還要帶一下估計的方法與常用數字
所以之後還有兩篇要整理
取捨
- 效能 vs 可擴展性
- 效能: 服務更多的工作單元,也可以處理更多的資料
- 性能問題: 對單一使用者來說的感覺是慢的
- 可擴展性: 服務性能的增加和資源的投入是成正比
- 可擴展性問題: 對於單一使用者來說感覺較快,但在高負載的時候就會變慢
- 效能: 服務更多的工作單元,也可以處理更多的資料
- 延遲 vs 吞吐量
- 延遲: 執行一個操作或運算結果所花費的時間
- 吞吐量: 單位時間內執行此類型操作或運算的數量
- 可接受的延遲數量下的最大化吞吐量 為設計目標
- 可用性 vs 一致性
- CAP 理論: 在一個分散式系統中,只能滿足以下三個項目的任兩項
- 一致性: 每次讀取都可以得到最新的資料,但偶爾會拿到錯誤
- 弱一致性
- 在寫入之後,任何的存取不一定可以拿到資料,弱一致性將盡力確保能存取到最新的資料 (錯過就沒了)
- 例子: 視訊聊天、藍芽耳機的延遲
- 最終一致性
- 在寫入後的讀取操作最終可以看到被寫入的資料資料透過非同步的方式被複製
- 例子: DNS、NoSQL (需要等一段時間傳播)
- 強一致性
- 在寫入後,讀取將立刻取得資料,資料是透過同步的方式寫入
- 例子: FileSystem、SQL
- 弱一致性
- 可用性: 每次讀取都可以得到非錯誤的回應,但不能保證可以得到最新的資料
- 容錯轉移
- 主動到備用切換
- 當 heartbeat 中斷時,備用的機器就會切換為主動機器的 IP 位置接替服務
- 只有處於主動的機器會處理使用者來的流量
- 雙主動切換
- 兩台伺服器都會負責處理流量
- 流量會在他們之間進行分散負載
- 缺點
- load balancer或是服務需要知道這些主動的機器
- 缺點
- 增加額外的硬體與複雜度
- 如果在新寫入的資料被複製到備用的機器前系統就發生故障,那有可能會遺失資料
- 主動到備用切換
- 複寫
- 主動到備用複寫(主從複寫)
- 主資料庫負責讀和寫
- 將寫入的資料複寫至一或多個從屬資料庫中
- 從屬資料庫只負責讀取
- 從屬資料庫可以再將寫入複製到更多以樹狀結構的其他資料庫中
- 如果主資料庫離線了
- 系統可以以只讀模式運行,直到
- 某個從屬資料庫被提升為主資料庫 OR
- 新的主資料庫出現
- 系統可以以只讀模式運行,直到
- 主資料庫負責讀和寫
- 雙主動複寫(主動模式複寫)
- 兩個主要的資料庫都負責讀取和寫入,並且兩者互相協調
- 如果主資料庫離線了,系統可以繼續運作
- 缺點
- load balancer或是服務需要知道這些主動的機器,決定寫入哪台
- 大多數無法保證一致性(違反 ACID)
- 因同步而產生寫入延遲
- 隨著更多寫入節點的增加和延遲的提高,如何解決衝突變複雜
- 缺點
- 複製 上的問題
- 如果在主要資料庫複製到其他結點前系統就失效,則會有資料丟失的可能
- 多讀取節點的問題
- 當有過多寫入時,讀取的資料庫可能會因為過多寫入操作而被阻塞,導致讀取功能異常
- 當讀取的資料庫越多時,需要複寫的資料庫越多,將會導致較為嚴重的延遲
- 在某些資料庫系統中,寫入主資料庫的操作可以用多執行緒來並行寫入,但讀取的資料庫只支援單一執行緒來循序寫入
- 複雜度
- 複寫意味著更多的硬體以及更高的複雜度
- 複製 上的問題
- 主動到備用複寫(主從複寫)
- 容錯轉移
- 部分容錯性: 在任意分區的網路故障情況下,系統仍然能夠持續運行
- 一致性: 每次讀取都可以得到最新的資料,但偶爾會拿到錯誤
- 網路是不可靠的,你的設計必須要確保部分容錯性,所以你只能夠在一致性與可用性中做出取捨
- CP: 一致性與部分容錯性
- 可能因超時而報錯
- AP: 可用性與部分容錯性
- 都拿的到資料,但不一定是最新的 (拿到舊的不是錯誤)
- CP: 一致性與部分容錯性
- CAP 理論: 在一個分散式系統中,只能滿足以下三個項目的任兩項
工具
- DNS
- NS, MX, CNAME, A record
- load balance
- Weighted round robin
- 基於延遲分流
- 基於地理位置分流
- 缺點
- DNS的query會有延遲
- DNS管理複雜
- 會被DDOS
- CDN
- 推送式 CDNs (callback)
- 當你的伺服器有檔案變動時,推送 CDN 會接收到新的變動內容
- 流量較小的網站,或是內容不是經常更新的網站使用推送式的 CDN 相當適合
- 可以設定檔案內容什麼時候過期以及何時更新,檔案內容只有在變更或新增的時候才會推送,最小化流量,但最大化儲存
- 拉取式 CDNs (on-demand)
- 當地一個使用者來請求該資源時,才從伺服器上抓取對應檔案
- 拉取式 CDN 可以節省儲存空間
- 在過期的文件被更新之前,則會導致多餘的流量
- 拉取式的 CDN 適合高流量的網站,因為檔案會被平均的分散在各個結點伺服器中
- 缺點
- CDN 的成本取決於流量,可能會因為成本而放棄使用
- 如果在 TTL 過期之前就更新內容,CDN 的緩存內容可能會過期
- 需要改變靜態內容的網址來指向 CDN
- 推送式 CDNs (callback)
- load balancer
- 分配方式
- 隨機
- 最少負載
- Session/cookies
- Weighted round robin
- 第四層負載平衡
- 第四層的負載平衡器會監看 傳輸層 的資訊來決定如何分發請求 (5 tuple)
- 透過 網路地址轉換(NAT) 來向上游的伺服器轉發資料
- 第七層負載平衡
- 第七層的負載平衡器會監看 應用層 來決定如何分發請求這包含了請求的 header、訊息和 cookies (DPI)
- 第四層的負載平衡比起第七層的所要花費的時間和計算資源更低
- 水平擴展
- 缺點
- 水平擴展會增加複雜性,同時也涉及了多台伺服器的議題
- 快取伺服器或資料庫需要隨著伺服器的增加而進行擴展,以便處理更多的請求
- 缺點
- 好處
- SSL Termination: 將傳入的請求解密,並且加密伺服器的回應
- Session 保存: 發行 cookie,並將特定使用者的請求路由到同樣的後端伺服器上
- 缺點
- 當負載平衡器資源不夠或沒有正確設定時,他可能會成為效能的瓶頸
- 會增加架構的複雜性
- 只有一台負載平衡器時,一樣有單點失敗的問題
- 分配方式
- reversed proxy
- 好處
- 隱藏後端伺服器的資訊、可以設定 IP 的黑名單、限制每個客戶端的連線數量等
- 增加可擴展性與靈活性 - 客戶端只會看到反向代理伺服器的 IP 或域名
- SSL 終止 - 解密傳入的請求、加密伺服器的回應,這樣後端伺服器就不需要進行這些高成本的操作
- 壓縮 - 壓縮伺服器的回應
- 快取 - 直接在代理伺服器回應命中快取的結果
- 缺點
- 引入反向代理伺服器會增加系統複雜度
- 只有一台反向代理伺服器會有單點失效的問題,而設定多台的反向代理伺服器(如 故障轉移 )同樣會增加系統複雜度
- 負載平衡器與反向代理
- 當有多台伺服器時,使用負載平衡非常有用,一般來說,負載平衡器會將流量路由給一組功能相同的伺服器上
- 即使只有一台伺服器或應用伺服器,反向代理也是有用的
- 好處
- container orchestration platform
- 微服務
- 可以獨立運作、小型的模組化服務每個服務會透過明確定義好的輕量級溝通機制,運作在一個獨立的流程中來共同實現一個目標
- 服務發現
- 透過註冊的名稱、位置、Port 等資訊來幫助各個服務發現彼此
- Health checks 可以幫助確認服務的完整性以及是否經常使用一個 HTTP 的路徑
- 缺點
- 設計多個鬆耦合微服務所組成的應用層,必須從架構、維運、流程等多個面向來考量,相對於單系統而言會非常不同
- 微服務會增加部署與維運的複雜度
- 微服務
- DB
- RDBMS(SQL)
- 特性: ACID
- 原子性 - 每一個資料庫事務操作要不就是全部完成,要不就是全部不完成
- 一致性 - 任何一個資料庫事務操作都會讓資料庫從一個有效的狀態轉換到另外一個有效狀態
- 隔離性 - 併發執行資料庫事務操作的結果會和循序執行的結果一致
- 持久性 - 一旦一個事務被資料庫執行後,他的結果與影響是擁永久保存的
- 複寫
- 見 CAP中的可用性的複寫
- federative database
- 將資料庫按照對應的功能進行分割
- 好處
- 減少每個資料庫寫入與讀取的流量,進而降低複製的延遲
- 較少的資料意味者更多適合放入記憶體中的資料,進而增加快取命中率
- 因為沒有循序寫入的中央式主資料庫,你可以並行寫入以增加吞吐量
- 缺點
- 如果你的資料表需要大量的功能和資料表,聯邦式資料庫的效率並不好
- 需要更新應用程式的邏輯來決定如何讀取和寫入到哪個資料庫
- 透過 server link 從兩個資料庫中關資料更加複雜
- 聯邦式資料庫需要更多的硬體和額外的複雜度
- Sharding
- 將資料分配在不同的資料庫上,使每個資料庫只管理整個資料的部分子集
- 好處
- 可以減少讀取和寫入的流量、減少複製並提高快取命中率
- 索引的容量也會減少,如此一來可以改善查詢的效能
- 當一個分片出現問題時,其餘的仍然可以正常運作
- 分片的機制並沒有中央式的資料庫,你可以並行寫入以增加吞吐量
- 缺點
- 為了避免資料遺失,你可能需要思考其他複寫的機制
- 你需要修改應用程式的邏輯來實作分片,這可能會導致 SQL 變得複雜
- 不合理的分片可能會導致資料負載不均
- 從多個分片中操作資料會很複雜
- 分片需要額外的硬體和複雜度
- Denormalization
- 不同資料表中的重複資料來避免高成本的 Join 操作
- 好處
- 反正規化可以避免,如處理跨資料中心 Join 操作
- 資料會重複存取
- Constraints 的機制可以讓重複的資料保持同步,但這樣會增加資料庫設計的複雜度
- 反正規化的資料庫在大量寫入負載的情況下,性能表現可能會比正規化的資料庫差
- SQL Tuning
- improve schema
- use properly index
- index特性
- 設定索引時,會將資料放置於記憶體中,會佔用更多記憶體空間
- 索引通常是使用平衡 B 樹 表示,這樣可以保證資料是有序的,並允許在對數時間內進行搜尋、循序訪問以及插入、刪除等操作
- SELECT, GROUP BY, ORDER BY, JOIN 中使用有index的欄位,來加速查詢
- 缺點
- 寫入操作會變慢,因為索引會需要更新
- 當讀取大量資料時,禁用索引再讀取,之後再重新建立索引
- 寫入操作會變慢,因為索引會需要更新
- index特性
- split table
- 將熱門的資料拆分到單獨的資料表中可以增加快取
- avoid complex joining
- 反正規化
- 特性: ACID
- NoSQL
- 特性: BASE
- Basicly Avalibility: 系統保證可用性
- Soft state: 系統的狀態可能隨著時間改變,即使在沒有輸入的情況下也是如此
- eventually consistent - 經過一段時間之後,在沒有收到任何輸入的情況下,系統最終會達到一致
- 鍵-值對 (hash table)
- 通常用來儲存簡單的資料模型或是頻繁修改的資料
- 文件類型 (文件當做值的value的hash table)
- 文件類型的資料庫具備高度靈活性(不會看到一堆NULL),通常用於處理偶爾變化的資料
- 列儲存型 (巢狀的 Map
ColumnFamily<RowKey, Columns<ColKey, Value, Timestamp>>
)- 原本是一個ID得到 row(
<item1, item2, ...>
) - 現在是一個item對到一個ID,可以當成把table依column切開
- 列儲存型態的資料的提供了高可用和高擴展性,通常被用在大量資料的儲存上
- 原本是一個ID得到 row(
- 圖形 (圖)
- 圖形資料庫針對表示外來鍵(Foreign Key)眾多的複雜關聯或多對多關聯進行優化
- 圖形資料庫為了儲存複雜的資料結構,例如社群網路,提供了很高的性能
- 特性: BASE
- SQL 或 NoSQL
- SQL
- 結構化資料、嚴格的 schema
- 需要複雜的 join
- transaction
- 既有資源豐富
- 有index
- NoSQL
- 半結構化資料、動態或具有彈性的 schema
- 不需要複雜的 joins
- 儲存 TB (或 PB) 等級的資料
- 高資料密集量的工作負載
- IOPS 的高吞吐量
- 適合使用 NoSQL
- 暫時性的資料
- 經常頻繁存取的資料
- SQL
- RDBMS(SQL)
快取
資料庫在資料均勻分布的情況下,讀取和寫入的效能是最好的 但是熱門的資料會讓讀取分佈不均,如此一來就會造成效能瓶頸 在資料庫前增加一個快取,就可以減少負載不均和突發流量所造成的影響
- 客戶端 (作業系統或瀏覽器)
- CDN
- DB
- 應用程式 (基於記憶體的快取,像是 Memcached 和 Redis)
- 快取的級別
- 資料庫查詢
- 記錄級別
- 查詢級別
- 注意
- 當你的查詢很複雜時,很難刪除快取內容
- 如果某個資料表中的某個欄位值改變時,需要刪除所有可能包含該欄位值的快取結果
- 物件 (應該避免文件檔案的快取,因為這會讓複製和自動擴展變得困難)
- 完整的可序列化物件
- 完整的 HTML
- 注意
- 如果物件內的基本資料已經改變,那應該要從快取中刪除這個物件
- 允許異步處理:workers 透過使用最新的快取來組裝物件
- 建議快取的資料
- 使用者 sessions
- 完整渲染的頁面
- 活動資訊
- 使用者資料圖表
- 資料庫查詢
- 快取的級別
- 快取策略
- Write around
- 應用程式負責從儲存裝置中進行讀取及寫入快取不直接和儲存裝置進行互動
- 過程
- 讀快取、拉資料、寫到快取 (讀的都一樣,下面就pass)
- 直接寫到DB
- 缺點
- 當請求的資料不在快取中時,就需要經過三個步驟來獲得資料,這會導致明顯的延遲
- 如果資料庫中的資料被更新了,會導致快取中的資料過時,這需要透過設定 TTL 強制更新快取,或透過Write through來解決這種問題
- 當快取的某個節點發生故障時,會需要被一個新的節點取代,這會導致延遲
- Write through
- 應用程式使用快取當作主要的資料儲存服務,將資料寫入/讀取到快取中,由快取服務負責向資料庫讀寫資料
- 過程
- 寫快取、寫到資料庫
- 缺點
- 當發生故障或因為水平擴展而產生新的節點時,新的節點中將不會有快取資料,直到資料庫更新為止
- 快取模式和寫入模式一起使用可以減緩這種現象
- 被寫入多數的資料可能永遠都不會被讀取
- 可以設定 TTL 來解決這種問題
- 當發生故障或因為水平擴展而產生新的節點時,新的節點中將不會有快取資料,直到資料庫更新為止
- Write back
- 應用程式使用快取當作主要的資料儲存服務,將資料寫入/讀取到快取中,由快取服務負責向資料庫*-非同步的*-讀寫資料
- 過程
- 寫快取、寫到Queue、return、(等一下)寫到DB
- 缺點
- 快取可能在資料成功寫入到儲存單元前就丟失
- 事後寫入比起快取模式或是直寫模式在實作上更為複雜
- 更新式快取
- 將快取設定為在到期之前就自動更新為最新存取的內容 (由server推資料)
- 過程
- server寫到快取、等client讀
- 缺點
- 無法準確預測未來會使用的資料時,會導致性能降低,還不如使用其他模式
- Write around
- 缺點
- 需要保持快取和資料庫之間資料的一致性,比如說要如何設定 快取無效
- 需要更改應用程式程式碼來支援像是 Redis 或 Memcached 等快取服務
- 快取的無效性是個難題,而什麼時候要更新快取就是個對應的複雜問題
非同步機制
- 訊息佇列
- 訊息佇列用來接收、保留以及傳遞訊息
- 使用者不會被阻塞,同時工作會在背景完成
- Backpressure
- 當佇列開始明顯成長時,佇列的大小可能會超過記憶體,這會導致無法命中快取
- 背壓 可以用來限制佇列的大小,讓佇列保持高吞吐率和良好的回應時間
- 一旦佇列滿了,客戶端將會得到 HTTP 503 的回應碼,以便讓他們在稍後重新嘗試
- 缺點
- 簡單的運算和需要即時的工作可能更適合使用同步運算,導入佇列可能會增加延遲或系統複雜度
- 訊息可能不會照發送順序完成
- 想想TCP為什麼需要ACK
- 同樣的訊息被重複處理
- 通常訊息佇列會讓處理失敗的訊息重新入列 (扣款?)