Rocksdb Slice使用中的一个小坑
本文記錄一下使用Rocksdb Slice過程中的一個小小坑,差點沒一口老血吐出來。
rocksdb的Slice 數據結構是一個小型得不可變類string數據結構,設計出來的目的是為了保證rocksdb內部處理用戶輸入的key在從內存到持久化磁盤的整個處理鏈路是不會被修改的,比較方便得從user_key 解碼出internal_key。
如果把這個數據結構當作普通字符串來使用,會有意想不到的 收獲。
Slice數據結構代碼如下:
class Slice {public:// Create an empty slice.Slice() : data_(""), size_(0) {}// Create a slice that refers to d[0,n-1].Slice(const char* d, size_t n) : data_(d), size_(n) {}// Create a slice that refers to the contents of "s"/* implicit */Slice(const std::string& s) : data_(s.data()), size_(s.size()) {}#ifdef __cpp_lib_string_view// Create a slice that refers to the same contents as "sv"/* implicit */Slice(std::string_view sv) : data_(sv.data()), size_(sv.size()) {}
#endif// Create a slice that refers to s[0,strlen(s)-1]/* implicit */Slice(const char* s) : data_(s) {cout << "called Slice(const char* s)" << endl;size_ = (s == nullptr) ? 0 : strlen(s); }// Create a single slice from SliceParts using buf as storage.// buf must exist as long as the returned Slice exists.Slice(const struct SliceParts& parts, std::string* buf);// Return a pointer to the beginning of the referenced dataconst char* data() const { return data_; }// Return the length (in bytes) of the referenced datasize_t size() const { return size_; }// Return true iff the length of the referenced data is zerobool empty() const { return size_ == 0; }// Return the ith byte in the referenced data.// REQUIRES: n < size()char operator[](size_t n) const {assert(n < size());return data_[n];}...// Return a string that contains the copy of the referenced data.// when hex is true, returns a string of twice the length hex encoded (0-9A-F)// Return a string that contains the copy of the referenced data.std::string ToString(bool hex = false) const {std::string result; // RVO/NRVO/moveif (hex) {result.reserve(2 * size_);for (size_t i = 0; i < size_; ++i) {unsigned char c = data_[i];result.push_back(toHex(c >> 4));result.push_back(toHex(c & 0xf));}return result;} else {result.assign(data_, size_);return result;}} ......// Compare two slices and returns the first byte where they differsize_t difference_offset(const Slice& b) const;// private: make these public for rocksdbjni accessconst char* data_;size_t size_;// Intentionally copyable
};
主要是其維護了一個const char* data_成員變量,當我們用戶通過db->Put(opts, "key","value",)時會調用Slice(const std::string& s)構造函數,并不會維護一個新的存儲空間,而是使用本來的默認構造函數為data_分配的地址空間。
坑就來了,也就是想要通過如下方式創建一個新的Slice變量:
map<Slice,int, MetaCmp> mp;
void AddSlice(const Slice& buf, const int& value) {if(mp.find(buf) != mp.end()) {mp[buf] = value;} else {mp.insert(pair<Slice, int>(buf, value));}
}int main() {for(int i = 0;i < 10; i++) {Slice str("meta" + to_string(i));AddSlice(str, i);}return 0;
}
會發現每次創建的Slice都會放在一個地址中,你以為你創建了9個Slice,并且存放到了一個map中,但實際上Slice的數據地址其實只有一個內容。
called default constructor 0x10e756290
called Slice(const std::string& s) meta0 0x7ffee14b5738
called Slice(const std::string& s) meta1 0x7ffee14b5738
called Slice(const std::string& s) meta2 0x7ffee14b5738
called Slice(const std::string& s) meta3 0x7ffee14b5738
called Slice(const std::string& s) meta4 0x7ffee14b5738
called Slice(const std::string& s) meta5 0x7ffee14b5738
called Slice(const std::string& s) meta6 0x7ffee14b5738
called Slice(const std::string& s) meta7 0x7ffee14b5738
called Slice(const std::string& s) meta8 0x7ffee14b5738
called Slice(const std::string& s) meta9 0x7ffee14b5738
key: meta9 value: 9
也就是當我們調用Slice str("meta" + to_string(i));構造一個str對象時,實際存放數據的內存空間內容就已經被修改了,后續的AddSlice函數已經其內部添加到一個map中的操作其實都是在針對已存在的key進行的操作。
歸根結底就是其維護了一個const char* data_;成員變量,它約束了當我們啟動一個進程時data_指針指向的內容只能是常量區域的一個固定的地址,并不會為各位新開辟一個存儲空間。
以上代碼切換成我們的string 時,則每一次構造都會重新分配一個新的地址來存放我們的內容。
坑~~~
總結
以上是生活随笔為你收集整理的Rocksdb Slice使用中的一个小坑的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 上海欢乐谷生日有优惠吗
- 下一篇: Linux NUMA 架构 :基础软件工