RocksDB调研和研究我爱编程

RocksDB系列三:Block Cache

2018-07-16  本文已影响1人  薛少佳

  Block Cache是RocksDB把数据缓存在内存中以提高读性能的一种方法。开发者可以创建一个cache对象并指明cache capacity,然后传入引擎中。cache对象可以在同一个进程中宫多个DB Instance使用,这样开发者就可以通过配置控制所有的cache使用。Block cache存储的是非压缩的数据块内容。用户也可以设置另外一个block cache来存储压缩数据块。读数据时首先从非压缩数据块cache中读数据、然后读压缩数据块cache。当Direct-IO打开的话,压缩数据库可以作为系统页缓存的替代。
  RocksDB中有两种cache的实现方式,分别为LRUCache和CLockCache。这两种cache都会被分片,来降低锁压力。用户设置的容量平均分配给每个shard。默认情况下,每个cache都会被分片为64块,每块大小不小于512K字节。

Usage

  默认情况下,RocksDB使用LRU cache实现,大小为8M。需要定制化设置一个block cache时,可以调用NewLRUCache() or NewClockCache()来创建对象实例,并设置到block based table options。用户也可以使用自己的cache(需要实现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));

  要设置压缩block cache:

table_options.block_cache_compressed = another_cache;

   如果block_cache设置为nullptr的话,RocksDB会创建默认的block cache。
   关闭block cache

table_options.no_block_cache = true;

LRU Cache

  默认情况,RocksDB使用LRU Cache,默认大小为8M。cache的每个分片都有自己的LRU list和hash表来查找使用。每个shard都有个mutex来控制数据并发访问。不管是数据查找还是数据写入,线程都要获取cache分片的锁。开发中也可以调用NewLRUCache()来创建一个LRU cache。这个函数提供了几个有用的配置项来设置cache:

Clock Cache

  ClockCache实现了CLOCK算法。CLOCK CACHE的每个shard都有一个cache entry的圆环list。算法会遍历圆环的所有entry寻找unspined entry来回收,但是如果上次scan操作这个entry被使用的话,也会有继续留在cache中的机会。寻找并回收entry使用tbb::concurrent_hash_map。
  使用LRUCache的一个好处是有一把细粒度的锁。在LRUCache中,即使是查找操作也需要获取分片锁,因为有可能会更改LRU-list。在CLock cache中查找并不需要获取分片锁,只需要查找当前hash_map就可以了,只有在insert时需要获取分片锁。使用clock cache,相比于LRU cache,写吞吐有一定提升。

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%

通过调用NewCLockCache()来串讲一个clock cache。为了使clock cache生效,RocksDB需要链接上Intel TBB库。当创建clock cache时,也有一些可以配置的信息。

Caching Index and Filter Blocks

  默认情况下,index和filter blocks都缓存在block cache外部,而且用户不能控制需要多少内存来cache这个block,当然如果用户设置了max_open_files配置除外。开发者可以将index和filter block数据cache在block cache中,以便更好地控制内存占用。
  要想把index和filter block数据cache 在block cache中

BlockBasedTableOptions table_options;
table_options.cache_index_and_filter_blocks = true;

  如果将index和filter blocks数据cache 在block cache中,这些block就需要和数据block竞争留在cache中。尽管index和filter blocks在内存中比数据block访问地更频繁,但是存在完全竞争失败的场景。这不是符合预期的,因为index和filter blocks往往比数据block更大,而且具有更高优先级留在cache中。

Simulated Cache

SimCache是当cache capacity或者shard num发生改变时预测cache hit的方法。SimCache封装了真正的Cache 对象,运行一个shadow LRU cache模仿具有同样capacity和shard num的cache服务,检测cache hit和miss。这个工具在下面这种情况很有用,比如:开发者打开了一个DB 实例,配置了4G的cache size,现在想知道如果将cache size调整到64G时的cache hit。
创建一个sim cache:

// 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;

Statistics

如果blick cache 计数表不为空的话,可以通过Options.statistics来访问。

// total block cache misses
// REQUIRES: BLOCK_CACHE_MISS == BLOCK_CACHE_INDEX_MISS +
//                               BLOCK_CACHE_FILTER_MISS +
//                               BLOCK_CACHE_DATA_MISS;
BLOCK_CACHE_MISS = 0,
// total block cache hit
// 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
上一篇 下一篇

猜你喜欢

热点阅读