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分析:
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
相关文章推荐
- 积分不等式证明的三种方法_20160416
- java 集合比较
- Jenkins+maven+svn+tomcat操作手册(四)
- 设计模式C++学习笔记之八(Adapter适配器模式)
- 精彩的javascript对象和数组混合相加
- 进程间的几种通信方式
- 单周期cpu设计与实现
- [codeforce]位运算——最大异或
- 弧长积分公式的证明_20160414
- 转行做程序员
- struts2中的constant配置详解
- Spring AOP原理及拦截器
- Spark1.6.1集群环境搭建——Standalone模式HA
- scala学习(2)——变量的使用
- LINUX 内存结构
- leetcode 318. Maximum Product of Word Lengths
- 《电路基础》一阶电路的零输入响应
- 华里士公式的推导_20160412
- 【一天一道LeetCode】#35. Search Insert Position
- 【一天一道LeetCode】#35. Search Insert Position