LevelDB源码剖析之Cache(HashTable LRUCache ShardedLRUCache)

Cache为接口类。为实际的缓存操作类ShardedLRUCache提供接口。


 ShardedLRUCache封装了16个LRUCache缓存片,每次对缓存的读取、插入、查找、删除操作都对应到所封装的相应缓存片LRUCache的相应方法完成。ShardedLRUCache提供了对应缓存片的方法。 


 LRUCache为一个循环双链表,与标准实现一致。其”头结点“lru_的prev始终指向最新节点,next始终指向最久未用节点,其对象容器为HashTable,用于为LRUCache提供快速的查找操作。


LRUHandle为节点类。其 next_hash指针用于HashTable中的bucket单向链表 。next和prev指针用于LRUCache循环双向链表的后继和前驱。


源码:

Cache接口类:

class Cache {
 public:
  Cache() { }
  // Destroys all existing entries by calling the "deleter"
  // function that was passed to the constructor.
  virtual ~Cache();

  struct Handle { };

  //插入一个键_值到缓存,随之可能改变整个缓存的容量。返回句柄。
  //当调用者不再需要<span style="font-family: Arial, Helvetica, sans-serif;">键_值</span><span style="font-family: Arial, Helvetica, sans-serif;">该时应调用Release(handle)引用计数减为0时,作为参数传入的deleter会删除该条目。</span>

  virtual Handle* Insert(const Slice& key, void* value, size_t charge,
                         void (*deleter)(const Slice& key, void* value)) = 0;

  //查找
  virtual Handle* Lookup(const Slice& key) = 0;//当没有该key时,返回null

//递减引用计数
  virtual void Release(Handle* handle) = 0;

<pre name="code" class="cpp">//返回其value<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
virtual void* Value(Handle* handle) = 0;

 
<pre name="code" class="cpp">//注意:只有当该条目的所有句柄都释放后才会真正删除该条目
virtual void Erase(const Slice& key) = 0;
//创建一个计数id,多个用户共享该缓存的关键域
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&);};
 

LRUHandle节点类:
struct LRUHandle {
  void* value;
  void (*deleter)(const Slice&, void* value);//资源回收函数
  LRUHandle* next_hash;// 用于hash table中的bucket单向链表 纵向 
  LRUHandle* next;//双向链表 前驱   横向
  LRUHandle* prev;//双向链表 后继
  size_t charge;      // TODO(opt): Only allow uint32_t?
  size_t key_length;//key的长度
  uint32_t refs;//节点的引用计数
  uint32_t hash;      // Hash of key(); used for fast sharding and comparisons  即位于哪个bucket
  char key_data[1];   // Beginning of key  key的指针

  Slice key() const {//获取key
    // For cheaper lookups, we allow a temporary Handle object
    // to store a pointer to a key in "value".
	  //对于临时节点,让其value存放其key的指针
    if (next == this) {//无后继节点(临时节点)
      return *(reinterpret_cast<Slice*>(value));
    } else {
      return Slice(key_data, key_length);
    }
  }
};


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

  //在hash的bucket中查找key的节点
  LRUHandle* Lookup(const Slice& key, uint32_t hash) {
    return *FindPointer(key, hash);
  }

  //插入节点   如果原来有则返回其节点指针,否则返回null
  LRUHandle* Insert(LRUHandle* h) {
    LRUHandle** ptr = FindPointer(h->key(), h->hash);
    LRUHandle* old = *ptr;
    h->next_hash = (old == NULL ? NULL : old->next_hash);//如果原来没有,则插入到最后,否则替换(old的前驱节点的处理??)
    *ptr = h;
    if (old == NULL) {
      ++elems_;
      if (elems_ > length_) {//若总元素个数过大,则需要调整bucket的个数 length_
                             //每个bucket的长度<=1,即元素总数<=bucket数量
        Resize();
      }
    }
    return old;
  }
  //如果存在则返回其节点指针,否则返回null
  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_;//bucket的个数
  uint32_t elems_;//总的节点数 所有bucket中的节点之和
  LRUHandle** list_;//哈希指针数组

  LRUHandle** FindPointer(const Slice& key, uint32_t hash) {
    LRUHandle** ptr = &list_[hash & (length_ - 1)];
    while (*ptr != NULL &&          //在对应的bucket中从上往下循环查找
           ((*ptr)->hash != hash || key != (*ptr)->key())) {
      ptr = &(*ptr)->next_hash;
    }
    return ptr;
  }

  void Resize() {
    uint32_t new_length = 4;//注意:每次调整都 翻倍增长
    while (new_length < elems_) {
      new_length *= 2;
    }
    LRUHandle** new_list = new LRUHandle*[new_length];//从新分配哈希表指针数组
	memset(new_list, 0, sizeof(new_list[0]) * new_length);//每个LRUHandle*置为null
    uint32_t count = 0;
    for (uint32_t i = 0; i < length_; i++) {//  重新hash,一次全部执行完毕  
      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)];//找到新哈希表的bucket
        h->next_hash = *ptr; //放到新的bucket的开头  
        *ptr = h;
        h = next;
        count++;
      }
    }
    assert(elems_ == count);
    delete[] list_;//释放原来的哈希表  注意:节点的引用计数
    list_ = new_list;
    length_ = new_length;
  }
};



 LRUCache:

//单片缓存类  <span style="font-family: Arial, Helvetica, sans-serif;">循环双向链表     对象容器为 hashtable</span>

class LRUCache {
 public:
  LRUCache();
  ~LRUCache();
<pre name="code" class="cpp">//设置容量
void SetCapacity(size_t capacity) { capacity_ = capacity; }

 
</pre><pre name="code" class="cpp">  //插入节点
  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);
<pre name="code" class="cpp">//释放引用计数
void Release(Cache::Handle* handle);
 
<pre name="code" class="cpp">//删除节点
void Erase(const Slice& key, uint32_t hash); private: void LRU_Remove(LRUHandle* e);//删除节点 void LRU_Append(LRUHandle* e);//插入节点到”头“之前 void Unref(LRUHandle* e);//递减引用计数 size_t capacity_;//容量 port::Mutex mutex_;//互斥访问 size_t usage_;//已用容量 uint64_t last_id_; //每次新加节点放到最前面 LRUHandle lru_;//假 头结点(循环双向链表) HandleTable table_;//对象容器};
 
  
ShardedLRUCache: 

//实际对外缓存片封装类(LRUCache数组)
class ShardedLRUCache : public Cache {//Cache 接口类
 private:
  LRUCache shard_[kNumShards];
  port::Mutex id_mutex_;
  uint64_t last_id_;

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

  //映射到哪个缓存CRUCache片
  static uint32_t Shard(uint32_t hash) {//hash值的高4位
    return hash >> (32 - kNumShardBits);
  }

 public:
	 //构造函数
  explicit ShardedLRUCache(size_t capacity)
      : last_id_(0) {
    const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;//每片缓存容量
    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_);
  }
};
























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值