尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

LevelDB 批量扫描时如何避免污染 Block Cache:fill_cache 选项的正确用法

LevelDB 批量扫描时如何避免污染 Block Cache:fill_cache 选项的正确用法 LevelDB 批量扫描时如何避免污染 Block Cachefill_cache 选项的正确用法【免费下载链接】leveldbLevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.项目地址: https://gitcode.com/GitHub_Trending/leveldb4/leveldb当你要对 LevelDB 做一次全量或大范围的批量扫描bulk scan时会遇到一个问题扫描读到的每个 block 默认都会被写入 block cache把热点数据挤出去导致后续随机读的性能退化。本文的任务是在 LevelDB 的 C API或 C API中完成一次批量扫描同时让扫描数据不进入 block cache。做法只有一个核心操作——把迭代器的ReadOptions::fill_cache设为false这是 doc/index.md 在 Cache 一节中官方给出的 bulk read 方案。问题背景block cache 的缓存机制先明确几个来自文档的事实它们决定了污染是怎么发生的用户数据以 block 为单位组织block 是磁盘读写的单位doc/index.mdBlock size 一节打开数据库时如果Options::block_cache非空它会被用来缓存解压后的 block 内容如果为nullptrLevelDB 会自动创建并使用一个 8MB 的内部 cacheinclude/leveldb/options.h内置 cache 是 LRU 淘汰策略include/leveldb/cache.h容量固定。批量扫描顺序读过的 block 会逐个插入 cache容量不够时就把热点 block 逐出。fill_cache的控制点就在读 block 时。table/table.cc 中block 读出后只有满足contents.cachable options.fill_cache才会执行block_cache-Insert(...)未缓存的 block 则通过迭代器的清理机制RegisterCleanup(DeleteBlock, ...)见同文件 table/table.cc在迭代器推进/析构时释放不会滞留在内存里。ReadOptions::fill_cache默认值为true头文件注释明确写道Should the data read for this iteration be cached in memory? Callers may wish to set this field to false for bulk scans.include/leveldb/options.h。LevelDB 自身也是这么用的内部 compaction 读取输入文件时db/version_set.ccVersionSet::MakeInputIterator和 dump 整个 table 的工具db/dumpfile.cc都把fill_cache设为false。第一步打开数据库并指定 block cache要观测是否污染 cache首先得自己持有 cache 对象。用NewLRUCache创建固定容量 cache 并赋给Options::block_cache这是 doc/index.md Cache 一节的写法#include cassert #include leveldb/cache.h #include leveldb/db.h leveldb::Options options; options.create_if_missing true; options.block_cache leveldb::NewLRUCache(100 * 1048576); // 100MB cache leveldb::DB* db; leveldb::Status status leveldb::DB::Open(options, /tmp/testdb, db); assert(status.ok());说明/tmp/testdb是文档示例中的数据库目录替换成你的目录即可cache 按解压后的数据大小来估容量不要按压缩后大小估算doc/index.md 明确要求按应用层数据尺寸规划。数据库用完后依次delete db和delete options.block_cache文档示例的顺序。第二步用 fill_cache false 的迭代器做批量扫描扫描时创建ReadOptions把fill_cache设为false再传给DB::NewIterator。这段代码直接取自 doc/index.md 的 bulk read 示例leveldb::ReadOptions options; options.fill_cache false; leveldb::Iterator* it db-NewIterator(options); for (it-SeekToFirst(); it-Valid(); it-Next()) { // 处理 it-key() / it-value()返回 leveldb::Slice } assert(it-status().ok()); // Check for any errors found during the scan delete it;两点使用细节fill_cache是每次读取的选项不是数据库级开关。同一个 DB 上不设置它的读操作点查Get、普通迭代器仍然会填充 cache只有这次批量扫描的读不填充。如果只扫描一个键范围而不是全库用 doc/index.md Iteration 一节给出的范围写法即可fill_cache的设置方式不变leveldb::ReadOptions roptions; roptions.fill_cache false; leveldb::Iterator* it db-NewIterator(roptions); for (it-Seek(start); it-Valid() it-key().ToString() limit; it-Next()) { // ... } delete it;其中start、limit由调用方提供扫描范围是[start, limit)。第三步验证扫描正确且 cache 未被灌入文档提供了两个可核对的检查点扫描本身没有出错循环结束后assert(it-status().ok())这是 doc/index.md 迭代示例中明确的检查方式。cache 占用没有随扫描增长Cache接口提供TotalCharge()文档定义为 Return an estimate of the combined charges of all elements stored in the cacheinclude/leveldb/cache.h。在扫描前后各取一次做对比size_t charge_before options.block_cache-TotalCharge(); // ... 执行上面 fill_cache false 的批量扫描 ... size_t charge_after options.block_cache-TotalCharge();TotalCharge()返回的是估算值且期间其他正常读点查、其他迭代器仍可能往 cache 里写内容所以不要把它当作精确的字节差值来断言它的作用是判断扫描读过的数据量是否整体进入了 cache。对照实验很简单同一段扫描把fill_cache改回true再跑一次TotalCharge()的增长会明显不同由此可以确认fill_cache false生效。C API 下的等价写法如果项目通过 C 接口include/leveldb/c.h使用 LevelDB对应的是leveldb_readoptions_set_fill_cache参数为 0 表示不填充leveldb_readoptions_t* roptions leveldb_readoptions_create(); leveldb_readoptions_set_fill_cache(roptions, 0); /* 用 roptions 创建迭代器并扫描结束后 leveldb_readoptions_destroy(roptions); */仓库的 C 接口测试 db/c_test.c 中即以此方式构造了不填 cache 的读选项。边界与限制fill_cache只影响是否写入 block cache不影响扫描速度、内存峰值中 block 本身的生命周期管理未缓存的 block 由迭代器清理见前文 table/table.cc。如果Options::block_cache为nullptrLevelDB 使用的是内部自动创建的 8MB cacheinclude/leveldb/options.h此时你拿不到 cache 句柄也就无法用TotalCharge()做上面的对比验证要验证污染问题建议显式传入自己的 cache。缓存的是解压后数据压缩块的缓存交给操作系统 buffer cache 或自定义 Envdoc/index.md Cache 一节这与fill_cache无关。批量扫描还会连带影响 compaction 读放大与磁盘压力但这些不在本选项的作用范围内本文只解决扫描数据挤占 block cache这一件事。【免费下载链接】leveldbLevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.项目地址: https://gitcode.com/GitHub_Trending/leveldb4/leveldb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表