Memcached 原理剖析

CrazyZard發表於2020-06-30

Memcached 以下簡稱 Mc

Mc 並不是將所有資料放在一起來進行管理的,而是將記憶體劃分為一系列相同大小的 slab 空間後,每個 slab 只管理一定範圍內的資料儲存。也就是說 Mc 內部採用 slab 機制來管理記憶體分配。 Mc 內的記憶體分配以 slab 為單位,預設情況下一個 slab 是 1MB,可以通過 -I 引數在啟動時指定其他數值。

slab 空間內部,會被進一步劃分為一系列固定大小的 chunk。每個 chunk 內部儲存一個 Item,利用 Item 結構儲存資料。因為 chunk 大小固定,而 key/value 資料的大小隨機。所以,Item 儲存完 key/value 資料後,一般還會有多餘的空間,這個多餘的空間就被浪費了。為了提升記憶體的使用效率,chunk size 就不能太大,而要儘量選擇與 key/value size 接近的 ,從而減少 chunk 內浪費的空間。

Mc 在分配記憶體時,先將記憶體按固定大小劃分成 slab,然後再將不同 slab 分拆出固定 size 的 chunk。雖然 slab 內的 chunk 大小相同,但不同 slab 的 chunk size 並不同,Mc 會按照一個固定比例,使劃分的 chunk size 逐步增大,從而滿足不同大小 key/value 儲存的需要。

一組具有相同 chunk size 的所有 slab,就組成一個 slabclass。不同 slabclass 的 chunk size 按遞增因子一次增加。Mc 就通過 slabclass 來管理一組 slab 內的儲存空間的。每個 slabclass 內部有一個 freelist ,包含這組 slab 裡所有空閒的 chunk,當需要儲存資料時,從這個 freelist 裡面快速分配一個 chunk 做儲存空間。當 Item 資料淘汰剔除時,這個 Item 所在的 chunk 又被回收至這個 freelist。

Memcached 的 slab 原理

Mc 在通過 slab 機制管理記憶體分配時,實際 key/value 是存在 Item 結構中,所以對 key/value 的儲存空間分配就轉換為對 Item 的分配。而 Item 空間的分配有 2 種方式,如果 Mc 有空閒空間,則從 slabclass 的 freelist 分配;如果沒有空閒空間,則從對應 slabclass id 對應的 LRU 中剔除一個 Item,來複用這個 Item 的空間。

在查詢或變更一個 key 時,首先要定位這個 key 所在的儲存位置。Mc 是通過雜湊表 Hashtable 來定位 key 的。Hashtable 可以看作是一個記憶體空間連續的大陣列,而這個大資料的每一個槽位對應一個 key 的 Hash 值,這個槽位也稱 bucket。由於不同 key 的 Hash 值可能相同,所以 Mc 在 Hashtable 的每個捅內部再用一個單向連結串列,來解決 Hash 衝突的問題。

Mc 內部是通過 LRU 來管理儲存 Item 資料的,當記憶體不足時,會從 LRU 隊尾中剔除一個過期或最不活躍的 key,供新的 Item 使用。

Mc 的系統架構主要包括網路處理模組、多執行緒處理模組、雜湊表、LRU、slab 記憶體分配模組 5 部分。Mc 基於 Libevent 實現了網路處理模組,通過多執行緒併發處理使用者請求;基於雜湊表對 key 進行快速定位,基於 LRU 來管理冷資料的剔除淘汰,基於 slab 機制進行快速的記憶體分配及儲存。

Memcached 原理剖析

網路處理模組

Mc 基於 Libevent 開發實現了多執行緒網路模型。Mc 的多執行緒網路模型分為主執行緒、工作執行緒。這些執行緒通過多路複用 IO 來進行網路 IO 接入以及讀寫處理。在 Linux 下,通常使用 epoll。通過多路複用 IO,特別是 epoll 的使用,Mc 執行緒無須遍歷整個被偵聽的描述符集,只要在被通知後遍歷 Ready 佇列的描述符集合就 OK 了。這些描述符是在各項準備工作完成之後,才被核心 IO 事件非同步通知。也就是說,只在連線做好準備後,系統才會進行事件通知,Mc 才會進行 I/O 操作。這樣就不會發生阻塞,使 Mc 在支援高併發的同時,擁有非常高的 IO 吞吐效率。

多執行緒處理模組

除了用於 IO 的主執行緒和工作執行緒外,還用於多個輔助執行緒,如 Item 爬蟲執行緒、LRU 維護執行緒、雜湊表維護執行緒等,通過多執行緒併發工作,Mc 可以充分利用機器的多個核心,實現很好的網路 IO 效能和資料處理能力。

雜湊表

  1. Mc 通過雜湊表即 Hashtable 來快速定位 key。資料儲存時,資料 Item 結構在存入 slab 中的 chunk 後,也會被存放到 Hashtable 中。同時,Mc 的雜湊表會在每個桶,通過 Item 記錄一個單向連結串列,以此來解決不同 key 在雜湊表中的 Hash 衝突問題。 當需要查詢給定 key 的 Item 時,首先計算 key 的 Hash 值,然後對雜湊表中與 Hash 值對應的 bucket 中進行搜尋,通過輪詢 bucket 裡的單向連結串列,找到該 key 對應的 Item 指標,這樣就找到了 key 對應的儲存 Item

Memcached 原理剖析

  1. 正常情況下,Mc 對雜湊表的插入、查詢操作都是在主表中進行的。當表中 Item 數量大於雜湊表 bucket 節點數的 1.5 倍時,就對雜湊表進行擴容。如下圖所示,擴容時,Mc 內部使用兩張 Hashtable,一個主雜湊表 primary_hashtable,一個是舊雜湊表 old_hashtable。當擴容開始時,原來的主雜湊表就成為舊雜湊表,而新分配一個 2 倍容量的雜湊表作為新的主表。擴容過程中,維護執行緒會將舊錶的 Item 指標,逐步複製插入到新主雜湊表。遷移過程中,根據遷移位置,使用者請求會同時查舊錶和新的主表,當資料全部遷移完成,所有的操作就重新回到主表中進行。

Memcached 原理剖析

LRU 機制

Mc 主要通過 LRU 機制,來進行冷資料淘汰的。自 1.4.24 版本之後,Mc 不斷優化 LRU 演算法,當前 Mc 版本已預設啟用分段 LRU 了。在啟用分段 LRU 之前,每個 slabclass id 只對應一個 COLD LRU,在記憶體不足時,會直接從 COLD LRU 剔除資料。而在啟用分段 LRU 之後,每個 slabclass id 就有 TEMP、HOT、WARM 和 COLD 四個 LRU。

如下圖所示,TEMP LRU 中 Item 剩餘過期時間通常很短,預設是 61 秒以內。該列隊中的 Item 永遠不會發生在佇列內搬運,也不會遷移到其他佇列。在插入新 key/value 時,如果 key 的剩餘過期時間小於 61 秒,則直接進入 TEMP LRU。後面,在必要時直接進行過期即可。這樣避免了鎖競爭,效能也更高。

Memcached 原理剖析

對於 HOT LRU,內部不搬運,當佇列滿時,如果隊尾 Item 是 Active 狀態,即被訪問過,那麼會遷移到 WARM 佇列,否則遷移到 COLD 佇列。

對於 WARM LRU,如果佇列的 Item 被再次訪問,就搬到隊首,否則遷移到 COLD 佇列。

對於 COLD LRU,存放的是最不活躍的 Item,一旦記憶體滿了,隊尾的 Item 會被剔除。如果 COLD LRU 裡的 Item 被再次訪問,會遷移到 WARM LRU。

slab 分配機制

Mc 通過 slab 機制來分配管理記憶體的,如下圖所示。可以說,slab 分配機制的使用,是 Mc 分配及儲存高效能的關鍵所在。在 Mc 啟動時,會建立 64 個 slabclass,但索引為 0 的 slabclass 做 slab 重新分配之用,基本不參與其他 slabclass 的日常分配活動。每個 slabclass 會根據需要不斷分配預設大小為 1MB 的 slab。

每個 slab 又被分為相同大小的 chunk。chunk 就是 Mc 儲存資料的基本儲存單位。slabclass 1 的 chunk size 最小,預設最小 chunk 的大小是 102 位元組,後續的 slabclass 會按照增長因子逐步增大 chunk size,具體數值會進一步對 8 取整。Mc 預設的增長因子是 1.25,啟動時可以通過 -f 將增長因子設為其他值。比如採用預設值,slabclass 1 的 chunk size 是 102,slabclass 2 的 chunk size 是 102×1.25,再對 8 取整後是 128。

Mc slab 中的 chunk 中通過 Item 結構存 key/value 鍵值對,Item 結構體的頭部存連結串列的指標、flag、過期時間等,然後存 key 及 value。一般情況下,Item 並不會將 chunk 填滿,但由於每個 key/value 在儲存時,都會根據 kev/value size,選擇最接近的 slabclass,所以 chunk 浪費的位元組非常有限,基本可以忽略。

每次新分配一個 slab 後,會將 slab 空間等分成相同 size 的 chunk,這些 chunk 會被加入到 slabclass 的 freelist 中,在需要時進行分配。分配出去的 chunk 儲存 Item 資料,在過期被剔除後,會再次進入 freelist,供後續使用。

Memcached 原理剖析

本作品採用《CC 協議》,轉載必須註明作者和本文連結

快樂就是解決一個又一個的問題!

相關文章