(InterviewBit) System Design - Design Cache System
前言
這是InterviewBit中Storage Scalabiliy第一題,設計一個快取系統。
我希望可以透過InterviewBit中的文章與題目,打開自己系統設計的視野,並且重裡面中得到關鍵詞彙,透過這些關鍵詞彙補足自己知識的不足。
其中會參考許多jyt0532神的許多文章
題目
替Twitter或是google設計快取系統,概念圖如下:
分析步驟
- 列出需求情境
- 估算
- 立定設計目標
- 深入探討設計方法
需求情境
- 需要多大的快取
- 我們的快取清除策略要如何
- 因為快取大小所以必須將舊資料刪掉
- 快取的寫入策略
- Write through cache
- 當資料寫入時,同時寫入DB和Cache中
- 寫入因為需要寫入兩個系統(DB/Cache)所以有較高的延遲
- Write around cache
- 當資料寫入時先寫入DB,當快取被讀取發現遺失資料時,再將資料從DB同步到快取中
- 因為讀取是從快取中讀取,但是在讀取時判斷到有新增資料沒進入快取,會有較高的讀延遲。
- 但寫和重複讀不會有特別的延遲
- Write back cache
- 直接將資料寫入到快取中,在同步到資料庫裡
- 風險 - 畢竟快取是in-memory會有遺失風險
- 優點 - 高寫入效率(寫得快可附合的流量大)
- 可以透過replica快取降低memory消失的風險
- Write through cache
評估
預先知識
- QPS - Queries Per Second
- TPS - Transactions Per Second
- UV - Unique Visitor
- RPS - Requests Per Secornd
獲取設計前條件
目前得知的條件
- 快取大小會是30TB
- QPS會是10M(每秒一千萬)
估算機器數量
假設我們每台機器都有72G的記憶體,則要存入30TB的資料則需要
30TB / 72G = 420(大約),而這是剛剛好的數目,有可能需要增加,因為我們每秒要附和10M的搜尋流量。
設計目標
設計目標三巨頭
- Latency(延遲)
- Consistency(一致性)
- Availability(可使用性)
快取系統的設計目標
快取的用意就在於降低延遲,DB的寫入是依靠硬碟的而快取是依靠記憶體,有基本計算機常識的話就可以知道記憶體的存取速度>>硬碟。所以快取系統顧名思義就是要降低延遲。
根據CAP理論,C和A我們只能偏重其一。
如何選擇
根據google和Twitter的業務性質,一致性並不會造成太大的影響(一時的不一致),因為在使用者觀看訊息時或許沒辦法看到最新的,但她依然可以看其他舊的訊息。
而可使用性就非常重要了,當快取系統不可用時也代表整個系統需要依靠DB去運作,整個搜尋或寫入的延遲就會非常的嚴重,造成使用者感受快速下降。
所以在C和A中我們取A為首先考慮要素。
深入探討設計細節
預先知識
快取策略 Wiki
- FIFO
- 最近進入Cache先淘汰,先進先出如同Queue,不過對於快取設計的概念,最近被使用也容易代表他還會持續被使用,所以FIFO出非特別業務不然不參考
- LFU
- 最近最少使用的快取先被淘汰
- 但是像是google搜尋引擎提示自,最近開始大家都搜尋這個關鍵字但是他在這個月是最少使用的,還是會被淘汰,所以還是需要依照業務邏輯
- LRU
- 最符合快取設計的策略
- (最久)沒有存取的資料淘汰
- (最久)沒有存取很大機率代表之後也不會存取
- NMRU
- 在最近沒有使用的資料隨機淘汰
快取最常使用的LRU如何設計
- 如果永遠不需要刪除過期的資料
- 我們可以直接使用一個Map or HashMap
- 若需要刪除最久沒被使用的資料
- Linkedlist會是個非常好的選擇
- 在每次Read資料時,接節點移到head
- 資料如此會自動根據使用調節順序
- 在快取超出最大容量時,刪除最後的節點
- Java LRU原始碼
在單執行緒時我們可以簡單地用上方的Java LRU實踐快取機制,但是當多個執行序至多台Server時該如何處理呢?
- 為何要討論多執行緒處理快取?
- 單執行緒時可以假設只是一台機器,而多執行緒放大後也可以想成分佈式系統
- 多執行緒牽涉到要如何設計鎖,讀和寫操作需要分離並且設計非同步的刷新方法
- 設計LinkedList與HashMap為基礎的快取資料結構
- LinkedList用於更新快取資訊(插入的順序性淘汰最末位)
- HashMap用於拿取(O(1)的速度提升快取的效率)
QPS的深入解析
現在我們每個機台都有自己的Cache方法了,重Memory層面來看完全沒問題!72G 420台機器,但是如果從QPS層面來看問題就大了,這個系統的QPS為10M,代表每台機器會附和到23000QPS,這可是不小的問題,我們該如何調整這個很吃緊的QPS呢?
計算每台機器QPS
通常情況Sever為四核心那代表我每台機器處理這個23000 QPS他的延遲最多只能
410001000/23000 = 174us,這包含了我們從記憶體新增修改的時間,還包含了伺服器接收request和回傳response的時間,明顯要我們在這時間內處理72G的記憶體快取是非常困難的。
所以我們可以讓機器只擁有16G記憶體但是數量擴增到4倍,這樣每台機器需要負責的QPS數也可以降到大約5500QPS,運算的數度也比較合理。
預先知識,了解每個硬體的儲存延遲
https://gist.github.com/jboner/2841832
總結
- 我們在設計過程中,分析了只從30T去分配機器數量會造成每台負荷QPS過高的問題
- 將每台記憶體下調,但是增加機器數量(增加運算能力)
- 我們思考了快取如何設計
- LinkedList + HashMap,實作直接使用java.util.LinkedListHashMap處理
- 快取的特性我們選擇了A不選擇C
- 快取重要性在於不要讓使用者直接碰觸到我們的DB(硬碟搜尋延遲>>>記憶體搜尋延遲),所以可使用性>一致性
- 這裡為何一致性權重會小於可使用性能?
- 因為系統特性,我們要設計的快取系統是給像是Twiter/google使用
- 其中的使用者操作會更注重於現在使否有資料回饋,而資料的一致性則可以等使用者看完手邊資料後刷新再補進去。
留言
張貼留言