leveldb源码剖析2.3–核心设计之文件缓存

引言

在leveldb中对文件数据缓存分为TableCache和Options中的block_cache,两个缓存的实现都是基于ShardedLRUCache类。

1 设计原理

1.1 核心思想

LRUCache主要缓存KV数据,其核心思想如下:

  • 首先,将Key通过哈希算法散列。然后通过哈希链表存储数据;为了解决多线程并发互斥加锁,所以分为16个隔离的桶去实现哈希链表的存储;
  • 最后,为了在当缓存达到上限删除最久未用的数据,采用链表将未使用的数据串列起来。当缓存超出时,从链表头剔除陈旧数据。

1.2 数据结构

整个数据节点LRUHandle分为三个维度,即HandleTable、LRUCache中的lru_和in_use_。如下图:
在这里插入图片描述
其中HandleTable是哈希表的实现;lru_是对未被外部引用的双向节点链表头;in_use_是正被外部引用的双向节点链表头。特别地,lru_和in_use_是HandleTable类型。

2 源码解读

2.1 核心代码

整个Cache的核心逻辑如下:
在这里插入图片描述

  • ShardedLRUCache实现Cahce接口,对外提供功能;每个ShardedLRUCache聚合16个LRUCache
  • 每个LRUCache聚合一个HandleTable,每个加入的LRUHandle节点以lru_和in_use_被串成链表;
  • 每个加入的LRUHandle节点也会以哈希链表形式聚合在HandleTable之中。从而便于快速查询和插入。

2.2 相关缓存

在leveldb中,主要有如下Cache对象:

  • TableCache
  • block_cache

2.2.1 TableCache

TableCache中key为文件序号,value为如下结构体:

struct TableAndFile {
    
    
  RandomAccessFile* file; //sstable文件操作指针
  Table* table; //访问sstable数据的操作对象
};

2.2.2 block_cache

block_cache中key为如下序列化结构:
在这里插入图片描述
block_cache中value为指向Block对象的指针。

2.3 可优化项

  • HashTable的Insert函数
  LRUHandle* Insert(LRUHandle* h) {
    
    
    LRUHandle** ptr = FindPointer(h->key(), h->hash);
    LRUHandle* old = *ptr;
    h->next_hash = (old == nullptr ? nullptr : old->next_hash);
    *ptr = h;
    if (old == nullptr) {
    
    
      ++elems_;
      if (elems_ > length_) {
    
    
        // 重新扩容哈希表
        Resize();
      }
    }
    return old;
  }
  • HashTable的Resize函数
  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);
    uint32_t count = 0;
    for (uint32_t i = 0; i < length_; i++) {
    
    
      LRUHandle* h = list_[i];
      //反转链表到新的哈希表中
      while (h != nullptr) {
    
    
        LRUHandle* next = h->next_hash;
        uint32_t hash = h->hash;
        LRUHandle** ptr = &new_list[hash & (new_length - 1)];
        h->next_hash = *ptr;
        *ptr = h;
        h = next;
        count++;
      }
    }
    assert(elems_ == count);
    delete[] list_;
    list_ = new_list;
    length_ = new_length;
  }

问题:
通过上图的代码发现,在HashTable扩容时需要遍历存储已加入的LRUHandle到新扩容的哈希表
优化:
可以将list_的数组结构改为红黑树存储,用红黑树实现哈希表功能。

猜你喜欢

转载自blog.csdn.net/fs3296/article/details/108123497