Leveldb中會使用cache來提高讀取性能,cache兩種數據,1,file meta信息,2,data block信息。
leveldb開放了cache的接口,用戶可以通過自定義cache類,完成對cache的定製化實現,另外leveldb定義了一個default cache,叫做LRU cache,在用戶未實現自定義cache類時,將使用此類作爲level的cache,本文脫離業務,單純分析LRU cache的實現.
首先概括此cache的特點,cache是爲了提高性能,用戶在獲取數據前(讀磁盤或者rpc)前,可以先通過在cache中查詢,用戶在存儲數據後,需要把最新數據寫入cache,或者失效之前的數據。
內存容量有限,所以cache需要限定capacity。如果插入的數據導致cache內存佔用超出了capacity,則需要選擇需要覆蓋的數據。這是內存cache設計最重要的方面之一。
LRUcache的特點是使用least recently used 策略來選擇需要覆蓋的數據,即,內存中優先保存最近使用過的數據,而覆蓋那些最久沒有使用過的數據
leveldb中的 LRUcache,關鍵點有
- 使用hashtable 來實現查找節點
- 使用兩個雙鏈表來區分數據新鮮度,in-use鏈表中保存新鮮數據,lru-list中保存已經很久沒有被使用的數據。通過節點的ref值來代表其新鮮度。
下面介紹具體類
LRUHandler
handler是cache中用到的基本存儲單元,有三個身份
- 保存了k-v數據
- 雙鏈表的節點
- hashtable的節點
// An entry is a variable length heap-allocated structure. Entries
// are kept in a circular doubly linked list ordered by access time.
struct LRUHandle {
void* value; //要cache的用戶數據
void (*deleter)(const Slice&, void* value); //釋放數據的方法
LRUHandle* next_hash;//hash表的指針
LRUHandle* next;//雙鏈表的指針
LRUHandle* prev;
size_t charge; // TODO(opt): Only allow uint32_t?
size_t key_length;
bool in_cache; // Whether entry is in the cache.
uint32_t refs; // References, including cache reference, if present.
uint32_t hash; // Hash of key(); used for fast sharding and comparisons
char key_data[1]; // Beginning of key
Slice key() const {
// For cheaper lookups, we allow a temporary Handle object
// to store a pointer to a key in "value".
if (next == this) {
return *(reinterpret_cast<Slice*>(value));
} else {
return Slice(key_data, key_length);
}
}
};
hash table
一個開鏈hash表。不同hash值的節點被組織在一條鏈表中保存,鏈表頭保存在一個數組中
hash table的主要方法包括
lookup
insert
remove
另外,當hash table中總的節點個數超過一定數值後,hash table將自動resize爲之前的兩倍大,並將舊數據重新hash到新table中,完成擴容。
LRUCache
LRU cache通過維護 hash table和兩個鏈表,來提供cache功能。
hash table和鏈表的節點是共享的,都是同一個LRU handler
in-use鏈表中索引ref值大於1的節點, lru鏈表中索引ref=1的節點
每次lookup查到的節點,其ref值會增1,此時可能觸發lru鏈表中的節點進入in-use鏈表
用戶使用完節點後,會調用unref,ref值會減一,可能觸發in-use鏈表中的節點轉移到lru鏈表中。
ShardedLRUCache
多個LRUCache 均衡負擔一個名字空間的所有數據的cache任務,組成一個shardedLRU cache
這樣可以降低併發查詢cache時的競爭,查詢同一個LRUCache時需要上鎖, 如果所有的查詢都發往同一個Cache,造成的競爭會比較嚴重。