WAL预写日志格式
概览
预写日志(WAL) 将memtable操作作为日志文件序列化到持久介质。在出现故障时,可以使用WAL文件通过从日志中重构memtable,将数据库恢复到一致状态。 当memtable被安全地清除到持久介质时,相应的WAL日志将被废弃并存档。 最后,经过一段时间后,将归档日志从磁盘中清除。
WAL 管理器
在WAL目录中,随着序列号的增加,将生成WAL文件。为了重建数据库的状态,按序号顺序读取这些文件。 WAL manager提供了将WAL文件作为单个单元读取的抽象。在内部,它使用Reader或Writer抽象打开和读取文件。
Reader/Writer 读取器/写入器
Writer提供了一个将日志记录附加到日志文件的抽象。特定于介质的内部细节由WriteableFile接口处理。 类似地,Reader提供了从日志文件中顺序读取日志记录的抽象。 内部媒体特定的细节由SequentialFile接口处理。
日志文件格式
日志文件由一系列可变长度的记录组成。记录按kBlockSize分组。如果某个记录不能放入剩余空间,那么剩余空间将填充空(null)数据。 写入器以kBlockSize的块进行编写,而读取器以kBlockSize的块进行读取。
+-----+-------------+--+----+----------+------+-- ... ----+
File | r0 | r1 |P | r2 | r3 | r4 | |
+-----+-------------+--+----+----------+------+-- ... ----+
<--- kBlockSize ------>|<-- kBlockSize ------>|
rn = variable size records
P = Padding
记录格式
记录布局格式如下所示。
+---------+-----------+-----------+--- ... ---+
|CRC (4B) | Size (2B) | Type (1B) | Payload |
+---------+-----------+-----------+--- ... ---+
CRC = 32bit hash computed over the payload using CRC
Size = Length of the payload data
Type = Type of record
(kZeroType, kFullType, kFirstType, kLastType, kMiddleType )
The type is used to group a bunch of records together to represent
blocks that are larger than kBlockSize
Payload = Byte stream as long as specified by the payload size
记录格式详情
日志文件内容是由32KB块组成的序列。唯一的例外是文件的尾部可能包含部分块。
每个数据块由一系列记录组成:
block := record* trailer?
record :=
checksum: uint32 // crc32c of type and data[]
length: uint16
type: uint8 // One of FULL, FIRST, MIDDLE, LAST
data: uint8[length]
记录永远不会在块的最后6个字节内开始(因为它不适合)。这里的任何剩余字节都构成预告片,它必须完全由零字节组成,并且必须被读者跳过。
如果正好七个字节在当前块,并添加一个新的非零长度记录,作家必须先发出记录(包含零字节的用户数据)来填满后7个字节的块,然后发出所有的用户数据在随后的街区。
将来可能会添加更多类型。一些读者可能跳过他们不理解的记录类型,另一些读者可能报告说跳过了一些数据。
FULL == 1
FIRST == 2
MIDDLE == 3
LAST == 4
FULL 完整记录包含整个用户记录的内容。
FIRST, MIDDLE, LAST 是用于被分割成多个片段的用户记录的类型(通常由于块边界)。 FIRST是用户记录的第一个片段的类型,LAST是用户记录的最后一个片段的类型,MID是用户记录的所有内部片段的类型。
例如:考虑一系列用户记录:
A: length 1000
B: length 97270
C: length 8000
A将作为完整的记录存储在第一个块中。
B将被分成三个片段:第一个片段占用第一个块的其余部分,第二个片段占用第二个块的全部,第三个片段占用第三个块的前缀。 这将在第三个块中留下6个字节的空闲空间,这将作为拖车保留为空。
C将作为完整记录存储在第四个块中。
优点
recordio格式的一些好处:
- 我们不需要任何启发式的重新同步-只要去下一个块边界和扫描。 如果有损坏,跳到下一个块。另一个好处是,当一个日志文件的部分内容作为记录嵌入到另一个日志文件中时,我们不会感到困惑。
- 在近似边界处进行分割(例如,对于mapreduce)很简单: 找到下一个块边界并跳过记录,直到找到完整的或第一个记录。
- 对于大型记录,我们不需要额外的缓冲。
缺点
与recordio格式相比,有些缺点:
- 没有记录的打包。可以通过添加新的记录类型来解决这个问题,所以这是当前实现的一个缺点,而不一定是格式的问题。
- 没有压缩。同样,可以通过添加新的记录类型来解决这个问题。