rocksdb-doc-cn

块缓存是Rocksdb在内存中缓存数据以用于读取的地方。用户可以带上一个期望的空间大小,传一个Cache对象给Rocksdb实例。一个缓存对象可以在同一个进程的多个RocksDB实例之间共享,这允许用户控制总的缓存大小。块缓存存储未压缩过的块。用户也可以选择设置另一个块缓存,用来存储压缩后的块。读取的时候会先拉去未压缩的数据块的缓存,然后才拉取压缩数据块的缓存。在打开直接IO的时候压缩块缓存可以替代OS的页缓存。

RocksDB里面有两种实现方式,分别叫做LRUCache和ClockCache。两个类型的缓存都通过分片来减轻锁冲突。容量会被平均的分配到每个分片,分片之间不共享空间。默认情况下,每个缓存会被分片到64个分片,每个分片至少有512kB空间。

使用

开箱即用的情况下,RocksDB会使用LRU块缓存实现,空间为8MB。如果希望使用自定义的块缓存,调用N额外LRUCache()或者NewClockCache()来创建一个缓存对象,然后把它设置到基于块的表选项。用户也可以使用自己实现的缓存,只需要实现Cache接口即可

std::shared_ptr<Cache> cache = NewLRUCache(capacity);
BlockBasedTableOptions table_options;
table_options.block_cache = cache;
Options options;
options.table_factory.reset(new BlockBasedTableFactory(table_options));

如果希望设置压缩块缓存

table_options.block_cache_compressed = another_cache;

如果block_cache是空指针,RocksDB会创建默认的块缓存。如果希望彻底关闭块缓存,你需要:

table_options.no_block_cache = true;

LRU缓存

开箱即用的情况下,RocksDB会使用LRU块缓存实现,空间为8MB。每个缓存分片都维护自己的LRU列表以及自己的查找哈希表。通过每个分片持有一个互斥锁来实现并发。不管是查找还是插入,都需要申请该分片的互斥锁。用户可以通过调用NewLRUCache创建一个LRU缓存。函数提供了一些非常有用的选项来设置缓存:

Clock缓存

ClockCache实现了CLOCK算法。每个clock缓存分片都维护一个缓存项的环形列表。一个clock指针遍历这个环形列表来找一个没有固定的项进行驱逐,同时,如果在上一个扫描中他被使用过了,那么给予这个项两次机会来留在缓存里。tbb::concurrent_hash_map 被用来查找数据。

与LRU缓存比较,clock缓存有更好的锁粒度。在LRU缓存下面,每个分片的互斥锁在读取的时候都需要上锁,因为他需要更新他的LRU列表。在一个clock缓存上查找数据不需要申请该分片的互斥锁,只需要搜索并行的哈希表就行了,所以有更好锁粒度。只有在插入的时候需要每个分片的锁。用clock缓存,在一定环境下,我们能看到读性能的增长。(参考cache/clock_cache.cc的注释以了解这个性能测试的设置)

Threads Cache     Cache               ClockCache               LRUCache
        Size  Index/Filter Throughput(MB/s)   Hit Throughput(MB/s)    Hit
    32   2GB       yes               466.7  85.9%           433.7   86.5%
    32   2GB       no                529.9  72.7%           532.7   73.9%
    32  64GB       yes               649.9  99.9%           507.9   99.9%
    32  64GB       no                740.4  99.9%           662.8   99.9%
    16   2GB       yes               278.4  85.9%           283.4   86.5%
    16   2GB       no                318.6  72.7%           335.8   73.9%
    16  64GB       yes               391.9  99.9%           353.3   99.9%
    16  64GB       no                433.8  99.8%           419.4   99.8%

如果需要构造一个clock缓存,调用NewClockCache,如果需要使用clock缓存,RocksDB需要与intel TBB库链接。clock缓存在构建的时候也有几个选项供用户选择:

缓存索引以及过滤块

默认情况下,索引和过滤块都在块缓存外面存储,并且,除了max_open_files,用户无法控制使用多少内存来缓存这些块。用户可以选择在块缓存中缓存索引和过滤块,这样可以更好的控制RocksDB的缓存。在块缓存中缓存索引和过滤块:

BlockBasedTableOptions table_options;
table_options.cache_index_and_filter_blocks = true;

通过把索引和过滤块放入块缓存,这些块需要和数据块竞争来留存在缓存中。尽管索引和过滤块的访问频率比数据块高,也有一些情况会出现相反的情况。这不是预期行为,因为索引和过滤块一般会比数据块更大,并且他们通常留存在内存的价值更大。有两个选项用来调优减轻这些问题:

模拟缓存

SimCache是一个工具,用来在缓存容量和数量变化的时候预测缓存命中率。他通过封装DB正在使用的真正的Cache对象,然后根据给定的容量和分片大小,运行一个后台的LRU缓存模拟,以测量缓存命中率和影子缓存的丢失率。这个工具在用户希望打开一个,比如说,有4GB的缓存大小的DB,但是希望知道如果缓存空间扩展到,比如说,64GB,的时候,缓存命中率会是多少?创建一个模拟缓存:

// This cache is the actual cache use by the DB.
std::shared_ptr<Cache> cache = NewLRUCache(capacity);
// This is the simulated cache.
std::shared_ptr<Cache> sim_cache = NewSimCache(cache, sim_capacity, sim_num_shard_bits);
BlockBasedTableOptions table_options;
table_options.block_cache = sim_cache;

模拟缓存需要的额外数据量小于sim_capacity的2%

统计

如果Options.statistics不是null,那么会有一系列的块缓存计数器可以被访问

// 总块缓存不明中数
// REQUIRES: BLOCK_CACHE_MISS == BLOCK_CACHE_INDEX_MISS +
//                               BLOCK_CACHE_FILTER_MISS +
//                               BLOCK_CACHE_DATA_MISS;
BLOCK_CACHE_MISS = 0,
// 总块缓存命中
// REQUIRES: BLOCK_CACHE_HIT == BLOCK_CACHE_INDEX_HIT +
//                              BLOCK_CACHE_FILTER_HIT +
//                              BLOCK_CACHE_DATA_HIT;
BLOCK_CACHE_HIT,
// # of blocks added to block cache.
BLOCK_CACHE_ADD,
// # of failures when adding blocks to block cache.
BLOCK_CACHE_ADD_FAILURES,
// # of times cache miss when accessing index block from block cache.
BLOCK_CACHE_INDEX_MISS,
// # of times cache hit when accessing index block from block cache.
BLOCK_CACHE_INDEX_HIT,
// # of times cache miss when accessing filter block from block cache.
BLOCK_CACHE_FILTER_MISS,
// # of times cache hit when accessing filter block from block cache.
BLOCK_CACHE_FILTER_HIT,
// # of times cache miss when accessing data block from block cache.
BLOCK_CACHE_DATA_MISS,
// # of times cache hit when accessing data block from block cache.
BLOCK_CACHE_DATA_HIT,
// # of bytes read from cache.
BLOCK_CACHE_BYTES_READ,
// # of bytes written into cache.
BLOCK_CACHE_BYTES_WRITE,

参考rocksdb内存使用#block-cache

看到这里或许你有建议或者疑问或者指出错误,请留言评论! 多谢! 你的评论非常重要!也可以帮忙点赞收藏转发!多谢支持! 觉得写的不错那就给点吧, 在线乞讨 微信转账