leveldb的缓存机制

leveldb采用LRU机制, 利用键的哈希值前n位作为索引, 将要插入的键值对分派到指定的缓存区, 当缓存区的使用率大于总容量后, 优先淘汰最近最少使用的缓存, 独立的缓存区总量为2^n .

初始化ShardedLRUCache

  • 设置初始缓存容量, 并设置16个子分区的容量.

      static const int kNumShardBits = 4;
    static const int kNumShards = 1 << kNumShardBits;
    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);
    }
    }

新建缓存

  • 由key的hash值的前kNumShardBits位作为子缓存区索引, 默认为前4位的索引位, 索引到指定的区, 子缓存区LRUCache接管新建缓存的任务.

      class ShardedLRUCache : public Cache {
    ...
    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);
    }
  • LRUCache结构

      // 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_; // Dummy head of LRU list.
    // lru.prev is newest entry, lru.next is oldest entry.
    LRUHandle lru_; HandleTable table_;
    };

    LRUCache才是真正具有缓存功能的结构, capacity_表示它的最大容量, mutex_规范各线程互斥地访问, usage_标志缓存中已用容量, lru_作为哑节点, 它的前节点是最新的缓存对象, 它的后节点是最老的缓存对象, table_是用来统计对象是否被缓存的哈希表.

  • LRUCache缓存生成

    1. 缓存的基础节点是LRUHandle, 储存节点的(键, 值, 哈希值, next, prev节点, 销毁处理函数等)信息. 综合上面的LRUCache结构也看到, 实际上, 缓存节点是双向列表存储, LRUHandle lru_这个哑节点用来分隔最常更新的节点和最不常更新节点.

    2. 当要将缓存节点插入缓存区, 先由哈希表判断缓存是否已存在, 若存在, 将其更新至最常更新节点; 若不存在, 则插入为最常更新节点. 同时更新哈希表HandleTable table_.

    3. 这里的HandleTable实现并没特殊之处, 我看是采用键哈希策略进行哈希, 如果键冲突则以链表进行存储:

    4. 利用二级指针对链表进行插入.

       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;
      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;
      }
  • 二级指针

    在HandleTable这个哈希表实现里, 有个FindPointer方法用来查找此对象是否已存在, 并返回LRUHandle**, 代码如下:

      // 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) {
    LRUHandle** ptr = &list_[hash & (length_ - 1)];
    while (*ptr != NULL &&
    ((*ptr)->hash != hash || key != (*ptr)->key())) {
    ptr = &(*ptr)->next_hash;
    }
    return ptr;
    }

    可见, 如果已存在节点, 则返回这个节点的LRUHandle**; 如果不存在, 返回的是可以保存这个LRUHandle*的地址.

  • LRUCache缓存查找

    如果缓存存在, 则将其放置到哑节点lru_的prev位置, 即最近使用节点, 相当于提升它的优先级, 便于下次快速查找; 如果不存在这个缓存, 返回NULL.

小结

看完leveldb的缓存实现, 在实现思路上, 是传统的lru算法, 使用哈希表判重, 根据缓存的请求时间提升缓存的优先级. 里面的细节例如使用哈希值的前n位进行路由, 路由到2^n 个独立的缓存区, 各个缓存区维护自己的mutex进行并发控制; 哈希表在插入节点时判断空间使用率, 并进行自动扩容, 保证查找效率在O(1).

levelDB缓存实现的更多相关文章

  1. MyCat源码分析系列之——BufferPool与缓存机制

    更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...

  2. leveldb 性能、使用场景评估

    最近有个业务写远远大于读,读也集中在最近写入,这不很适合采用leveldb存储么,leveldb业界貌似ssdb用得挺广,花了两天时间就ssdb简单做下测试,以下总结. ssdb 是leveldb的r ...

  3. 大型web系统数据缓存设计

    1. 前言 在高访问量的web系统中,缓存几乎是离不开的:但是一个适当.高效的缓存方案设计却并不容易:所以接下来将讨论一下应用系统缓存的设计方面应该注意哪些东西,包括缓存的选型.常见缓存系统的特点和数 ...

  4. LevelDB库简介

    LevelDB库简介 一.LevelDB入门 LevelDB是Google开源的持久化KV单机数据库,具有很高的随机写,顺序读/写性能,但是随机读的性能很一般,也就是说,LevelDB很适合应用在查询 ...

  5. HBase、Redis、MongoDB、Couchbase、LevelDB主流 NoSQL 数据库的对比

    最近小组准备启动一个 node 开源项目,从前端亲和力.大数据下的IO性能.可扩展性几点入手挑选了 NoSql 数据库,但具体使用哪一款产品还需要做一次选型. 我们最终把选项范围缩窄在 HBase.R ...

  6. 161104、NoSQL数据库:key/value型之levelDB介绍及java实现

    简介:Leveldb是一个google实现的非常高效的kv数据库,能够支持billion级别的数据量了. 在这个数量级别下还有着非常高的性能,主要归功于它的良好的设计.特别是LSM算法.LevelDB ...

  7. [转载] leveldb日知录

    原文: http://www.cnblogs.com/haippy/archive/2011/12/04/2276064.html 对leveldb非常好的一篇学习总结文章 郑重声明:本篇博客是自己学 ...

  8. 关于时间序列数据库的思考——(1)运用hash文件(例如:RRD,Whisper) (2)运用LSM树来备份(例如:LevelDB,RocksDB,Cassandra) (3)运用B-树排序和k/v存储(例如:BoltDB,LMDB)

    转自:http://0351slc.com/portal.php?mod=view&aid=12 近期网络上呈现了有关catena.benchmarking boltdb等时刻序列存储办法的介 ...

  9. Leveldb 实现原理

    原文地址:http://www.cnblogs.com/haippy/archive/2011/12/04/2276064.html LevelDb日知录之一:LevelDb 101 说起LevelD ...

随机推荐

  1. hdu 4888 Redraw Beautiful Drawings 网络流

    题目链接 一个n*m的方格, 里面有<=k的数, 给出每一行所有数的和, 每一列所有数的和, 问你能否还原这个图, 如果能, 是否唯一, 如果唯一, 输出还原后的图. 首先对行列建边, 源点向行 ...

  2. 64位WINDOWS系统环境下应用软件开发的兼容性问题(CPU 注册表 目录)

    应用软件开发的64 位WINDOWS 系统环境兼容性 1. 64 位CPU 硬件 目前的64位CPU分为两类:x64和IA64.x64的全称是x86-64,从名字上也可以看出来它和 x86是兼容的,原 ...

  3. mongodb初体验

    最近关注大数据,自然会关注到nosql数据库,其中当然是mongodb. nosql数据库大多是k,v数据库,这也不是新鲜的名词了,berkerly DB已经存在很多年了,现在属于oracle. 具体 ...

  4. HDU 5735 Born Slippy(拆值DP+位运算)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5735 [题目大意] 给出一棵树,树上每个节点都有一个权值w,w不超过216,树的根为1,从一个点往 ...

  5. Linux进程间通信总结

    刚请完婚假,请假期间做了些技术总结,其中一个就是Linux进程间通信方式的总结. Linux提供了多种进程间通信的方式,列举如下: PIPE(管道) FIFO(先进先出,也称为有名管道) domain ...

  6. 关于javascript面向对象之闭包

    要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量,而在函数外部无法 ...

  7. dos下遍历目录和文件的代码(主要利用for命令)

    对指定路径指定文件进行遍历的程序,这里有多个批处理代码,但运行好像有些问题,大家可以根据需要选择 ===== 文件夹结构 ======================================= ...

  8. ASP.NET之电子商务系统开发-1(数据列表)

    一.前言 首先声明的是,这是我第一个与别人合作的.net项目,另一个人做的是后台管理,我做的前台,这是一个电子商务的系统,主要实现的功能是查看商品以及购物功能. 二.开始 首先看一下我截取的项目部分商 ...

  9. JavaScriptCore.framework基本用法(二)

    接着上节我们讲到的iOS调用js 下来我们使用js调用iOS js调用iOS分两种情况 一,js里面直接调用方法 二,js里面通过对象调用方法 首先我们看第一种,直接调用方法. 其中用到了iOS的bl ...

  10. button 变成圆

      btn.layer.cornerRdius = width/2.0;btn.layer.maskToBounds = width/2.0: