前文回顾
文中“蓝色字体”部分均有跳转,大部分是引用了 Github 上的源码链接,可以点击【阅读原文】查看。
这里的 log 是指 Write Ahead Log[1]。前面说了,LevelDB 写入的数据会先保存到 MemTable。为了防止宕机导致数据丢失,在将数据写入 MemTable 之前,会先将数据持久化到 log 文件中。
本文主要参考 LevelDB 的文档 log_format.md[2]。
如上图所示,LevelDB 的 log 文件内容被组织成多个 32 KB[3] 的定长块(block)。每个 block 由 1~多个 record 组成(末尾可能会 padding)。一个 record 由一个固定 7 字节[4]的 header(checksum: uint32 + length: uint16 + type: uint8) 和实际数据(data: uint8[length])组成。
如果 block 的末尾不足 7 字节(小于 header 的大小),则全部填 0x00,读取的时候会被忽略。
如果 block 的末尾刚好 7 字节,则填充一个 length 为 0 的 record。
下面,我们将上层写入的数据称之为 user record,以区分 block 中的 record。由于 block 是定长的,而 user record 是变长的,一个 user record 有可能被截断成多个 record,保存到一段连续的 block 中。因此,在 header 中有一个 type 字段用来表示 record 的类型[5]:
enum RecordType {
// Zero is reserved for preallocated files
kZeroType = 0,
kFullType = 1,
// For fragments
kFirstType = 2,
kMiddleType = 3,
kLastType = 4
};
上面这样描述可能比较抽象,这里举个例子来说明一下。
初始化整个 log 为空,假设我们有 3 个 user records:
A 小于 32KB,会被保存到第一个 block,长度为 1000,类型为 kFullType,占用空间为 7 + 1000 = 1007。
B 比较大,会被切分成 3 个分片,保存到不同的 block:
C 会被保存到第四个 block,长度为 8000 字节,类型为 kFullType,占用空间 7 + 8000 = 8007。
综上,A、B、C 在 log 文件中的结构如下。
LevelDB 为什么采用这种定长块的方式保存日志呢?一个明显的好处就是,当日志文件发生数据损坏的时候,这种定长块的模式可以很简单地跳过有问题的块,而不会导致局部的错误影响到整个文件。
目前 LevelDB 没有对日志进行压缩。
LevelDB 读写日志的实现逻辑比较清晰,建议根据上面介绍的日志格式仔细看下 log_reader 和 log_write 的实现。
Write Ahead Log: https://en.wikipedia.org/wiki/Write-ahead_logging
[2]log_format.md: https://github.com/google/leveldb/blob/master/doc/log_format.md
[3]32 KB: https://github.com/google/leveldb/blob/1.22/db/log_format.h#L27
[4]7 字节: https://github.com/google/leveldb/blob/1.22/db/log_format.h#L30
[5]record 的类型: https://github.com/google/leveldb/blob/1.22/db/log_format.h#L14
[6]log_format.h: https://github.com/google/leveldb/blob/1.22/db/log_format.h
[7]log_reader.h: https://github.com/google/leveldb/blob/1.22/db/log_reader.h
[8]log_reader.cc: https://github.com/google/leveldb/blob/1.22/db/log_reader.cc
[9]log_writer.h: https://github.com/google/leveldb/blob/1.22/db/log_writer.h
[10]log_writer.cc: https://github.com/google/leveldb/blob/1.22/db/log_writer.cc