log的读涉及到的关键变量如图所示。
log文件在物理上是分块存储的,而在逻辑上一个个record存储的。
一个record可能跨越了几个block,所以每个record可能会被分为几个record,分别存储。
record的结构是[uint32_t crc32c校验][uint16_t 数据长度][uint8_t 类型]
log文件涉及到的record类型有
FULL == 1
FIRST == 2
MIDDLE == 3
LAST == 4
从字面意思我们可以很容易看出他们的作用。
这个类型可以有其他类型的,只是被log模块忽略。
log_read的对象不给复制,经典的写法
// No copying allowed
Reader(const Reader&);
void operator=(const Reader&);
log模块对数据的可靠性下了很多功夫,就是说,通过log_read读到的数据一定是正确的,
对于错误的数据它能自动抛弃掉,并且用户能自定义发现错误时的行为(例如将一些信息记录下来什么的)
class Reporter {
public:
virtual ~Reporter();
// Some corruption was detected. "size" is the approximate number
// of bytes dropped due to the corruption.
virtual void Corruption(size_t bytes, const Status& status) = 0;
};
Corruption这个虚函数可以用户自己定义,当然也可以使用缺省的,ldb自带的。
一个record的crc32c是根据type和数据的body进行计算,tpye的crc32c作为初始值。
crc32c有标准的算法,所以ldb在代码逻辑上将算crc32c的算法模块独立出来的,
而代码中对于crc32c的计算是通过一个extern函数引进来的,ldb中有很多这样的理念,
就是把一个独立方法模块,通过extern引进来。至于怎么样才算独立,后续的分析再说说吧。
extern uint32_t Extend(uint32_t init_crc, const char* data, size_t n);
而且对算出来的crc32c的值做了一个变换(自定义mask操作)
这样做有什么好处是:在别人不知你代码中mask的情况下,无法篡改你的数据。
前面说log模块是极度保证数据的可靠性的。这也算是一个佐证吧。
虚函数和extern是ldb中使用广泛的手段,下来将持续关注分析。
好吧,其他的产品也是广泛使用,只是使用的方法不同,但是各有各的理由。