首页 > Rocksdb 写入数据后 GetApproximateSizes 获取的大小竟然为0?

Rocksdb 写入数据后 GetApproximateSizes 获取的大小竟然为0?

项目开发中需要从引擎 获取一定范围的数据大小,用作打点上报,测试过程中竟然发现写入了一部分数据之后通过GetApproximateSizes 获取写入的key的范围时取出来的数据大小竟然为0。。。难道发现了一个bug?(欣喜)

因为写入的数据是小于一个sst的data-block(默认是4K),会不会因为GetApproximateSizes 对小于一个data-block的数据大小都默认是0?对于一个严谨的引擎,这么明显的问题显然不可忍。

问题代码:

#include 
#include #include 
#include #define VALUE_SIZE 100using namespace std;
using namespace rocksdb;void check_status(Status s, std::string op) { if (!s.ok()) { cout << " Excute " << op << " failed "<< s.ToString() << endl;exit(1);}
}static std::string Key(int i) { char buf[100];snprintf(buf, sizeof(buf), "key%06d", i);return std::string(buf);
}int main() { rocksdb::DB* db;rocksdb::Options options;rocksdb::Status s;options.create_if_missing = true;options.compression = kNoCompression;// 打开dbcheck_status(rocksdb::DB::Open(options, "./db", &db), "Open DB");// 写入10条key-value,value大小是100Bfor (int i = 0;i < 10; i++) { check_status(db->Put(WriteOptions(), Key(i), Slice(string(VALUE_SIZE, 'a' + (i % 26)))), "Put DB");}// 取其中的key范围为[1,3],获取处于这个范围的key-value大小uint64_t size;string start = Key(1);string end = Key(3);Range r(start, end);db->GetApproximateSizes(&r, 1, &size);cout << "Approximate size is " << size << endl; delete db;return 0;
}

最终的执行结果是:

Approximate size is 0

本来开开心心,很明显的问题,想要分析一下原因,向社区提一个PR,结果翻看了一下源代码就没心情了,还是自己太天真。

这个获取指定范围的key大小的接口是有一个额外参数的include_flags

  virtual void GetApproximateSizes(const Range* ranges, int n, uint64_t* sizes,uint8_t include_flags = INCLUDE_FILES) { GetApproximateSizes(DefaultColumnFamily(), ranges, n, sizes, include_flags);}

这个额外参数是用来指定从rocksdb的哪一个组件获取指定范围的key的大小,比如从memtable,或则 sst?

自己使用默认参数 写入了一小部分数据,显然没有达到触发flush的条件,都会存储在memtable,所以这里从默认的sst文件获取这个范围的key大小时显然获取不到。

可以继续看更底层的实现:

Status DBImpl::GetApproximateSizes(const SizeApproximationOptions& options,ColumnFamilyHandle* column_family,const Range* range, int n, uint64_t* sizes) { ......Version* v;auto cfh = static_cast_with_check<ColumnFamilyHandleImpl>(column_family);auto cfd = cfh->cfd();// 增加针对当前cf的引用SuperVersion* sv = GetAndRefSuperVersion(cfd);v = sv->current;// 允许同时传入多个range,这里对传入的range进行遍历for (int i = 0; i < n; i++) { Slice start = range[i].start;Slice limit = range[i].limit;// Add timestamp if neededstd::string start_with_ts, limit_with_ts;if (ts_sz > 0) { // Maximum timestamp means including all key with any timestampAppendKeyWithMaxTimestamp(&start_with_ts, start, ts_sz);// Append a maximum timestamp as the range limit is exclusive:// [start, limit)AppendKeyWithMaxTimestamp(&limit_with_ts, limit, ts_sz);start = start_with_ts;limit = limit_with_ts;}// Convert user_key into a corresponding internal key.InternalKey k1(start, kMaxSequenceNumber, kValueTypeForSeek);InternalKey k2(limit, kMaxSequenceNumber, kValueTypeForSeek);sizes[i] = 0;// 从sst文件中取指定key范围的大小if (options.include_files) { sizes[i] += versions_->ApproximateSize(options, v, k1.Encode(), k2.Encode(), /*start_level=*/0,/*end_level=*/-1, TableReaderCaller::kUserApproximateSize);}// 从memtable中取出指定key范围的大小,包括mem和immif (options.include_memtabtles) { sizes[i] += sv->mem->ApproximateStats(k1.Encode(), k2.Encode()).size;sizes[i] += sv->imm->ApproximateStats(k1.Encode(), k2.Encode()).size;}}// 释放对superversion的引用ReturnAndCleanupSuperVersion(cfd, sv);return Status::OK();
}

再对应到从sst文件的blockbased table中取数据,需要创建blockbased的index的iter来取start-end key所属的datablock的偏移地址。

如果要从memtable 中取数据,也就是需要遍历skiplist,顺序逐层遍历跳表,找到属于start-end范围内的所有key的个数,统一计算大小。

经过上面一轮的分析,我们就知道了想要通过GetApproximateSizes 获取准确的一个区间内的key-value大小,需要同时计算memtable+sst的大小,这才足够精确。

ps: 同样的数据放在memtable和放在sst中是不一样的,因为sst中除了data-block中key-value数据,还有indexblock,还有metaindex,还有footer。所以统计同样的数据在memtable和sst中会有一些差异。

最终正确使用GetApproximateSizes() 接口的方式如下:

#include 
#include #include 
#include #define VALUE_SIZE 100using namespace std;
using namespace rocksdb;void check_status(Status s, std::string op) { if (!s.ok()) { cout << " Excute " << op << " failed "<< s.ToString() << endl;exit(1);}
}static std::string Key(int i) { char buf[100];snprintf(buf, sizeof(buf), "key%06d", i);return std::string(buf);
}int main() { rocksdb::DB* db;rocksdb::Options options;rocksdb::Status s;options.create_if_missing = true;options.compression = kNoCompression;check_status(rocksdb::DestroyDB("./db", options),"DestroyDB");check_status(rocksdb::DB::Open(options, "./db", &db), "Open DB");for (int i = 0;i < 3; i++) { check_status(db->Put(WriteOptions(), Key(i), Slice(string(VALUE_SIZE, 'a' + (i % 26)))), "Put DB");}uint64_t size;string start = Key(1);string end = Key(3);Range r(start, end);db->GetApproximateSizes(&r, 1, &size);cout << "Approximate size is " << size << endl; uint8_t include_both = DB::SizeApproximationFlags::INCLUDE_FILES |DB::SizeApproximationFlags::INCLUDE_MEMTABLES;db->GetApproximateSizes(&r, 1, &size, include_both);cout << "After set memtable flag, Approximate size is " << size << endl; db->Flush(FlushOptions());db->GetApproximateSizes(&r, 1, &size);cout << "After flush, Approximate size is " << size << endl; delete db;return 0;
}

输出如下:

Approximate size is 0
After set memtable flag, Approximate size is 238
After flush, Approximate size is 1151

好吧,不用提bug了。。。。。。

更多相关:

  • 1. 定义网络的基本参数 定义输入网络的是什么: input = Input(shape=(240, 640, 3)) 反向传播时梯度下降算法 SGD一定会收敛,但是速度慢 Adam速度快但是可能不收敛 [link](https://blog.csdn.net/wydbyxr/article/details/84822806...

  • size_t和int       size_t是一些C/C++标准在stddef.h中定义的。这个类型足以用来表示对象的大小。size_t的真实类型与操作系统有关。 在32位架构中被普遍定义为: typedef   unsigned int size_t; 而在64位架构中被定义为: typedef  unsigned lo...

  • 我在 https://blog.csdn.net/wowricky/article/details/83218126 介绍了一种内存池,它的实现类似于linux 中打开slub_debug (1. make menuconfig: Kenel hacking -> Memory Debugging, 2. comand line中传入...

  • 文章目录概览1. UDB 架构2. UDB 表格式3. Rocksdb:针对flash存储优化过的第三方库3.1 Rocksdb架构3.2 为什么选择Rocksdb4. MyRocks / Rocksdb 开发历程4.1 设计目标4.2 性能挑战4.2.1 降低CPU的消耗4.2.2 降低range-scan 的延时消耗4.2.3 磁...

  • Compaction过程中 产生大量读I/O 的背景 项目中因大value 需求,引入了PingCap 参考Wisckey 思想实现的key-value分离存储 titan, 使用过程中因为有用到Rocksdb本身的 CompactionFilter功能,所以就直接用TitanDB的option 传入了compaction filt...

  • 简单记录一些 在linux下 统计进程内部函数运行耗时的统计工具,主要是用作性能瓶颈分析。当然以下工具除了pstack功能单一之外,其他的工具都非常强大,这里仅仅整理特定场景的特定用法,用作协同分析。 以下工具需要追踪具体的进程,如果想要打印信息更全,建议编译的时候将符号信息都编译到二进制文件之中,-g选项 strace str...

  • 想要自己随时随地写一写rocksdb的代码,并且快速测试,但是公司的物理机登陆过于麻烦,想要验证功能的话其实在自己的电脑就完全可以了。 安装 brew install rocksdb,默认二进制文件安装在/usr/local/bin在~/.bashrc或者自己正在使用的shell的rc文件中 加入rocksdb的bin文件所在路径...

  • importjava.security.SecureRandom;importjavax.crypto.Cipher;importjavax.crypto.SecretKey;importjavax.crypto.SecretKeyFactory;importjavax.crypto.spec.DESKeySpec;//结果与DES算...

  • 题目:替换空格 请实现一个函数,把字符串 s 中的每个空格替换成"%20"。 输入:s = "We are happy." 输出:"We%20are%20happy." 限制: 0 <= s 的长度 <= 10000 解题: 时间复杂度:O(n) 空间复杂度:O(n) class Solution { public:s...

  • 在C++11标准库中,string.h已经添加了to_string方法,方便从其他类型(如整形)快速转换成字面值。 例如: for (size_t i = 0; i < texArrSize; i++)RTX_Shader.SetInt(string("TexArr[") + to_string(i) + "]", 7 + i);...

  • Ubuntu 14.04安装并升级之后,变成楷体字体非常难看,我昨天搞了一晚上,终于理了个头绪,这里整理一下。 经过网上调研,大家的一致看法是,使用开源字体库文泉驿的微黑字体效果比较理想,甚至效果不输windows平台的雅黑字体。下面我打算微黑来美化Ubuntu 14.04. 1.安装文泉驿微黑字体库 sudo aptitude...

  • 使用string时发现了一些坑。 我们知道stl 容器并不是线程安全的,所以在使用它们的过程中往往需要一些同步机制来保证并发场景下的同步更新。 应该踩的坑还是一个不拉的踩了进去,所以还是记录一下吧。 string作为一个容器,随着我们的append 或者 针对string的+ 操作都会让string内部的数据域动态增加,而动态增加的...