您的位置:首页 > 其它

leveldb源码分析:Cache

2016-05-02 17:46 375 查看
cache.h只提供了接口,内部实现全部在cache.cc中。

cache.cc中:

LRUHandle是双向链表的节点结构

HandleTable是一个hash表,其冲突采用链表的方法解决。它的作用主要是管理LRUCache的节点。

LRUCache是一个采用LRU算法实现的Cache。

ShardedLRUCache是一个Cache的具体使用,其继承自Cache类,内部有LRUCache成员,操作都是通过对LRUCache的操作来完成的。这些都是类都是内部实现,对外不透明,对外只提供了Cache这一个类,所有操作接口也由Cache给出。

其中,LRUCache的结构大致如下:



cache.h分析:

#ifndef STORAGE_LEVELDB_INCLUDE_CACHE_H_
#define STORAGE_LEVELDB_INCLUDE_CACHE_H_

#include <stdint.h>
#include "leveldb/slice.h"

namespace leveldb {
//接口类,实际使用的Cache会继承自它,比如leveldb使用的ShardedLRUCache。
class Cache;

//新建一个Cache,其容量为capacity。
extern Cache* NewLRUCache(size_t capacity);

class Cache {
public:
Cache() { }

virtual ~Cache();

//访问cache内部结构的入口。
struct Handle { };

//将映射key->value插入到Cache中。不使用此Handle后要调用Release释放掉。
//Release的时候会调用deleter。
virtual Handle* Insert(const Slice& key, void* value, size_t charge,
void(*deleter)(const Slice& key, void* value)) = 0;

//查找映射key->value。
virtual Handle* Lookup(const Slice& key) = 0;

// Release a mapping returned by a previous Lookup().
// REQUIRES: handle must not have been released yet.
// REQUIRES: handle must have been returned by a method on *this.
//释放掉handle所持有的资源。
virtual void Release(Handle* handle) = 0;

// Return the value encapsulated in a handle returned by a
// successful Lookup().
// REQUIRES: handle must not have been released yet.
// REQUIRES: handle must have been returned by a method on *this.
//返回封装在handle中的value。
virtual void* Value(Handle* handle) = 0;

// If the cache contains entry for key, erase it.  Note that the
// underlying entry will be kept around until all existing handles
// to it have been released.
virtual void Erase(const Slice& key) = 0;

// Return a new numeric id.  May be used by multiple clients who are
// sharing the same cache to partition the key space.  Typically the
// client will allocate a new id at startup and prepend the id to
// its cache keys.
virtual uint64_t NewId() = 0;

private:
void LRU_Remove(Handle* e);
void LRU_Append(Handle* e);
void Unref(Handle* e);

struct Rep;
Rep* rep_;

// No copying allowed
Cache(const Cache&);
void operator=(const Cache&);
};

}  // namespace leveldb

#endif  // STORAGE_LEVELDB_UTIL_CACHE_H_


cache.cc分析:

namespace leveldb {

Cache::~Cache() {
}

//LRUHandle是双向链表的节点结构
//HandleTable是一个hash表,其冲突采用链表的方法解决。它的作用主要是管理LRUCache的节点。
//LRUCache是一个采用LRU算法实现的Cache。
//ShardedLRUCache是一个Cache的具体使用,其继承自Cache类,内部有LRUCache成员,操作都是通过对LRUCache的操作来完成的。
//这些都是类都是内部实现,对外不透明,对外只提供了Cache这一个类,所有操作接口也由Cache给出。
namespace {
//双向链表的节点结构。
struct LRUHandle {
void* value; //存放value
void(*deleter)(const Slice&, void* value);//删除结点时对key和value的处理。
LRUHandle* next_hash;//hash表也会用到LRUHandle结构,而hash表对冲突采用链表的解决方法,这个指针就指向冲突的数据。
LRUHandle* next;//双向链表的后一个节点。
LRUHandle* prev;//双向链表的前一个节点。
size_t charge;  //此节点占用的大小。
size_t key_length; //key的长度
uint32_t refs; //引用计数
uint32_t hash;  //key值的hash值。
char key_data[1]; //key值的开始地址。

Slice key() const {//返回key值。
// 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);
}
}
};

class HandleTable {
public:
HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); }
~HandleTable() { delete[] list_; }

LRUHandle* Lookup(const Slice& key, uint32_t hash) {
return *FindPointer(key, hash);
}

LRUHandle* Insert(LRUHandle* h) {
LRUHandle** ptr = FindPointer(h->key(), h->hash);
LRUHandle* old = *ptr;
//如果不存在,插入到槽的最后,否则插入到已存在的位置。
h->next_hash = (old == NULL ? NULL : old->next_hash);
*ptr = h;
//不存在,elems_加1,如果元素个数超过长度,resize,目的是让每个槽的平均长度小于等于1。
if (old == NULL) {
++elems_;
if (elems_ > length_) {
// Since each cache entry is fairly large, we aim for a small
// average linked list length (<= 1).
Resize();
}
}
return old;
}

LRUHandle* Remove(const Slice& key, uint32_t hash) {
LRUHandle** ptr = FindPointer(key, hash);
LRUHandle* result = *ptr;
if (result != NULL) {
//存放的指针变为下一个冲突节点。
*ptr = result->next_hash;
--elems_;
}
return result; //返回的节点并没有释放存储。
}

private:
uint32_t length_; //hash表的总长度。
uint32_t elems_; //hash表已使用的元素数。
LRUHandle** list_; //hash表指针。

// Return a pointer to slot that points to a cache entry that
// matches key/hash.  If there is no such cache entry, return a
// pointer to the trailing slot in the corresponding linked list.
LRUHandle** FindPointer(const Slice& key, uint32_t hash) {
//由于length_初始值为Resize中的4,且之后的值都是*2,为2的幂,
//所以可以看作对hash值取余,得到类似hash%length_。ptr最终为指向某一个槽的指针的地址。
LRUHandle** ptr = &list_[hash & (length_ - 1)];
while (*ptr != NULL && //如果要查询的hash值或者key值不同,继续往冲突的链表后面找。
((*ptr)->hash != hash || key != (*ptr)->key())) {
ptr = &(*ptr)->next_hash;
}
return ptr;
}

void Resize() {
uint32_t new_length = 4; //初始化长度为4
while (new_length < elems_) {
new_length *= 2; //找到首次大于elems_的2的幂。
}
LRUHandle** new_list = new LRUHandle*[new_length]; //新建一个hash表
memset(new_list, 0, sizeof(new_list[0]) * new_length);
uint32_t count = 0;
//把原来hash表中的元素拷贝到新hash表中
for (uint32_t i = 0; i < length_; i++) {
LRUHandle* h = list_[i];
while (h != NULL) {
LRUHandle* next = h->next_hash;
Slice key = h->key();
uint32_t hash = h->hash;
LRUHandle** ptr = &new_list[hash & (new_length - 1)]; //重新hash到新hash表中。
h->next_hash = *ptr;
*ptr = h;
h = next;
count++;
}
}
assert(elems_ == count);
delete[] list_;
list_ = new_list;
length_ = new_length;
}
};

// A single shard of sharded cache.
class LRUCache {
public:
LRUCache();
~LRUCache();

// Separate from constructor so caller can easily make an array of LRUCache
void SetCapacity(size_t capacity) { capacity_ = capacity; }

// Like Cache methods, but with an extra "hash" parameter.
Cache::Handle* Insert(const Slice& key, uint32_t hash,
void* value, size_t charge,
void(*deleter)(const Slice& key, void* value));
Cache::Handle* Lookup(const Slice& key, uint32_t hash);
void Release(Cache::Handle* handle);
void Erase(const Slice& key, uint32_t hash);

private:
void LRU_Remove(LRUHandle* e);
void LRU_Append(LRUHandle* e);
void Unref(LRUHandle* e);

// Initialized before use.
//容量
size_t capacity_;

// mutex_ protects the following state.
port::Mutex mutex_;
size_t usage_; //已使用的容量
uint64_t last_id_;

// Dummy head of LRU list.
// lru.prev is newest entry, lru.next is oldest entry.
//LRU表
LRUHandle lru_;
//hash表,里面存放lru_的各个节点的指针,加快查询速度。
HandleTable table_;
};

LRUCache::LRUCache()
: usage_(0),
last_id_(0) {
// Make empty circular linked list
//前后都指向自己的空链表。
lru_.next = &lru_;
lru_.prev = &lru_;
}

LRUCache::~LRUCache() {
for (LRUHandle* e = lru_.next; e != &lru_;) {
LRUHandle* next = e->next;
//析构时,调用者必须都已经释放掉了其拥有的handle。
assert(e->refs == 1);  // Error if caller has an unreleased handle
Unref(e);
e = next;
}
}

void LRUCache::Unref(LRUHandle* e) {
assert(e->refs > 0);
e->refs--;
if (e->refs <= 0) {
usage_ -= e->charge;
(*e->deleter)(e->key(), e->value);
free(e);
}
}

//移除节点e,不释放其存储。
void LRUCache::LRU_Remove(LRUHandle* e) {
e->next->prev = e->prev;
e->prev->next = e->next;
}

void LRUCache::LRU_Append(LRUHandle* e) {
// Make "e" newest entry by inserting just before lru_
e->next = &lru_;
e->prev = lru_.prev;
e->prev->next = e;
e->next->prev = e;
}

Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {
MutexLock l(&mutex_);
//通过hash表查找,加快速度。
LRUHandle* e = table_.Lookup(key, hash);
if (e != NULL) {
e->refs++; //e会返回给调用者,因此增加引用计数。
//将e删除,并添加到链表中,这样e为最新访问的节点。
LRU_Remove(e);
LRU_Append(e);
}
return reinterpret_cast<Cache::Handle*>(e);
}

void LRUCache::Release(Cache::Handle* handle) {
MutexLock l(&mutex_);
Unref(reinterpret_cast<LRUHandle*>(handle));
}

Cache::Handle* LRUCache::Insert(
const Slice& key, uint32_t hash, void* value, size_t charge,
void(*deleter)(const Slice& key, void* value)) {
MutexLock l(&mutex_);

LRUHandle* e = reinterpret_cast<LRUHandle*>(
malloc(sizeof(LRUHandle) - 1 + key.size())); //减1是因为key_data[1]也占用了一个字节。
e->value = value;
e->deleter = deleter;
e->charge = charge;
e->key_length = key.size();
e->hash = hash;
e->refs = 2;  // One from LRUCache, one for the returned handle
memcpy(e->key_data, key.data(), key.size());
LRU_Append(e); //把e加到LRU链表的开始。
usage_ += charge;

LRUHandle* old = table_.Insert(e); //把e加到hash表中
if (old != NULL) { //如果已经存在,在LRU链表中删除old节点,并减少引用计数。
LRU_Remove(old);
Unref(old);
}

//添加后已使用的容量超过设置的容量最大值,采用LRU算法,删除最久未使用的节点,直到满足容量要求。
while (usage_ > capacity_ && lru_.next != &lru_) {
LRUHandle* old = lru_.next;
//remove节点时,同时在LRU链表和hash表中都移除。
LRU_Remove(old);
table_.Remove(old->key(), old->hash);
Unref(old);
}

return reinterpret_cast<Cache::Handle*>(e);
}

void LRUCache::Erase(const Slice& key, uint32_t hash) {
MutexLock l(&mutex_);
LRUHandle* e = table_.Remove(key, hash);
if (e != NULL) {
LRU_Remove(e);
Unref(e);
}
}

static const int kNumShardBits = 4;
static const int kNumShards = 1 << kNumShardBits;

//此类接口基本都是对LRUCache接口的调用。
class ShardedLRUCache : public Cache {
private:
LRUCache shard_[kNumShards]; //16个Cache
port::Mutex id_mutex_;
uint64_t last_id_;

static inline uint32_t HashSlice(const Slice& s) {
return Hash(s.data(), s.size(), 0);
}

//取高kNumShardBits位。
static uint32_t Shard(uint32_t hash) {
return hash >> (32 - kNumShardBits);
}

public:
explicit ShardedLRUCache(size_t capacity)
: last_id_(0) {
const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;
//kNumShards个Cache平分容量。
for (int s = 0; s < kNumShards; s++) {
shard_[s].SetCapacity(per_shard);
}
}
virtual ~ShardedLRUCache() { }
virtual Handle* Insert(const Slice& key, void* value, size_t charge,
void(*deleter)(const Slice& key, void* value)) {
const uint32_t hash = HashSlice(key);
return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
}
virtual Handle* Lookup(const Slice& key) {
const uint32_t hash = HashSlice(key);
return shard_[Shard(hash)].Lookup(key, hash);
}
virtual void Release(Handle* handle) {
LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
shard_[Shard(h->hash)].Release(handle);
}
virtual void Erase(const Slice& key) {
const uint32_t hash = HashSlice(key);
shard_[Shard(hash)].Erase(key, hash);
}
virtual void* Value(Handle* handle) {
return reinterpret_cast<LRUHandle*>(handle)->value;
}
virtual uint64_t NewId() {
MutexLock l(&id_mutex_);
return ++(last_id_);
}
};

}  // end anonymous namespace

//新建LRUCache的接口,这样不必知道内部实现。
Cache* NewLRUCache(size_t capacity) {
return new ShardedLRUCache(capacity);
}

}  // namespace leveldb
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: