LevelDB之Slice
前言
LevelDB中出现最多的对象就是Slice了,他算的上是一个轻量级的string 对象,里面只有两个成员变量:
1 | const char* data_; |
- 在Slice 中看到一个c++的operator的关键字,这个关键字的作用就是重载运算。比如重载两个数据中的+
1
2
3
4
5
6
7
8
9
10 class MyClass {
public:
int value;
MyClass(int val) : value(val) {}
// Overloading + operator
MyClass operator+(const MyClass& other) {
MyClass result(value + other.value);
return result;
}
};如果有MyClass + MyClass的操作,就会执行重载里面的方法。如果是下面那种=的重载,则相当于重载了赋值= 的含义,后面使用default,则表示使用默认的复制函数
- const 在后面还有const修饰符,成员函数如果被声明为
const
,表示这个成员函数是一个不会修改对象状态的函数。也就是说,它不会修改调用该函数的对象的任何成员变量。感觉是不可变量的一样。
他的构造函数为:
1 | Slice() : data_(""), size_(0) {} //空slice, |
可以看到,他接受的最后基本上都是一个char的指针,以及当前在char指针中存储的数据length,需要获取数据,就直接去对应的char数组里面获取。本身没有创建内存或者释放内存,也就是说只是做为一个char数组的标识一样,本身不会产生内存和数据。
他的成员函数包括:
1 | // Return a pointer to the beginning of the referenced data |
slice的方法除了remove_prefix和clear以外都是非静态的即对Slice本身不做改变,所以在Slice.h
中作者写道
1
2
3
4 // Multiple threads can invoke const methods on a Slice without
// external synchronization, but if any of the threads may call a
// non-const method, all threads accessing the same Slice must use
// external synchronization.
也就是说只要没有调用非const方法,那么Slice 就是线程安全的。
Slice还有3个内联函数
内联方法是会将这个方法在编译阶段直接展开的方法,而不是需要方法调用
memcmp
是 C/C++ 标准库中的一个函数,用于比较两块内存区域的内容是否相等。它的声明如下:int memcmp(const void* ptr1, const void* ptr2, size_t num);
1 | inline bool operator==(const Slice& x, const Slice& y) { // 重载== 方法 |
why
为什么不使用string对象,而是使用Slice 作为char数组呢?在代码中的doc/index.md 中说明了原因:
Returning a Slice is a cheaper alternative to returning a
std::string
since we do not need to copy potentially large keys and values. In addition, leveldb methods do not return null-terminated C-style strings since leveldb keys and values are allowed to contain'\0'
bytes.
Slice 本身是不包含内存管理和分配的,而是直接应用外部的char数组指针,这样在操作的时候就避免了数据的复制和拷贝。主要是因为存储格式是按照length value的形势紧凑的存储的,如果key或者value的值较大,可能就是会从原始数据中截断,然后复制到string中,添加了额外的数据复制。
使用Slice 还有一个好处就是上文一直提到的,它本身不管理内存,只是对引用的数据,优化了内存的使用。
数据更加灵活,因为string本身是按照‘\0’作为结束符,但是在LevelDB中可能存在空的字节,这个是因为sequence等高位为空的情况,所以不太适合于string。PS:在debug的过程中,尤其是debug到skiplist插入的时候,就可能存在只能在clion中看到key,但是无法看到value的情况。就是因为string已经被截断了。
和string的切换十分方便,因为构造函数和tostring其实都是将size和data中的数据进行一个转换,能够灵活转换,而且更加灵活记录数据。
后记
Slice 其实就是一个仅记录char 数组头部指针和长度的数据类型,本身可以说不存储数据,个人感觉就是个start,end的结构来记录char 数组中的数据,然后就是提供了需要使用的比较和赋值等数据的方法。因为没有内存,但是可以灵活操作数组中的数据,这里的操作大部分都是线程安全的如比较或者获取的操作。写的操作较少。