日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

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

發(fā)布時間:2023/11/27 生活经验 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Rocksdb 写入数据后 GetApproximateSizes 获取的大小竟然为0? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

項目開發(fā)中需要從引擎 獲取一定范圍的數(shù)據(jù)大小,用作打點上報,測試過程中竟然發(fā)現(xiàn)寫入了一部分?jǐn)?shù)據(jù)之后通過GetApproximateSizes 獲取寫入的key的范圍時取出來的數(shù)據(jù)大小竟然為0。。。難道發(fā)現(xiàn)了一個bug?(欣喜)

因為寫入的數(shù)據(jù)是小于一個sst的data-block(默認(rèn)是4K),會不會因為GetApproximateSizes 對小于一個data-block的數(shù)據(jù)大小都默認(rèn)是0?對于一個嚴(yán)謹(jǐn)?shù)囊?#xff0c;這么明顯的問題顯然不可忍。

問題代碼:

#include <iostream>
#include <string>#include <rocksdb/db.h>
#include <rocksdb/slice.h>#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;
}

最終的執(zhí)行結(jié)果是:

Approximate size is 0

本來開開心心,很明顯的問題,想要分析一下原因,向社區(qū)提一個PR,結(jié)果翻看了一下源代碼就沒心情了,還是自己太天真。

這個獲取指定范圍的key大小的接口是有一個額外參數(shù)的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);}

這個額外參數(shù)是用來指定從rocksdb的哪一個組件獲取指定范圍的key的大小,比如從memtable,或則 sst?
自己使用默認(rèn)參數(shù) 寫入了一小部分?jǐn)?shù)據(jù),顯然沒有達(dá)到觸發(fā)flush的條件,都會存儲在memtable,所以這里從默認(rèn)的sst文件獲取這個范圍的key大小時顯然獲取不到。

可以繼續(xù)看更底層的實現(xiàn):

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();// 增加針對當(dāng)前cf的引用SuperVersion* sv = GetAndRefSuperVersion(cfd);v = sv->current;// 允許同時傳入多個range,這里對傳入的range進(jìn)行遍歷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();
}

再對應(yīng)到從sst文件的blockbased table中取數(shù)據(jù),需要創(chuàng)建blockbased的index的iter來取start-end key所屬的datablock的偏移地址。
如果要從memtable 中取數(shù)據(jù),也就是需要遍歷skiplist,順序逐層遍歷跳表,找到屬于start-end范圍內(nèi)的所有key的個數(shù),統(tǒng)一計算大小。

經(jīng)過上面一輪的分析,我們就知道了想要通過GetApproximateSizes 獲取準(zhǔn)確的一個區(qū)間內(nèi)的key-value大小,需要同時計算memtable+sst的大小,這才足夠精確。

ps: 同樣的數(shù)據(jù)放在memtable和放在sst中是不一樣的,因為sst中除了data-block中key-value數(shù)據(jù),還有indexblock,還有metaindex,還有footer。所以統(tǒng)計同樣的數(shù)據(jù)在memtable和sst中會有一些差異。

最終正確使用GetApproximateSizes() 接口的方式如下:

#include <iostream>
#include <string>#include <rocksdb/db.h>
#include <rocksdb/slice.h>#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了。。。。。。

總結(jié)

以上是生活随笔為你收集整理的Rocksdb 写入数据后 GetApproximateSizes 获取的大小竟然为0?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。