当向 LevelDB 写入数据时,数据会写入内存中的 MemTable,但是由于内存是易失性存储,掉电后数据就不再了,因此为了提升数据的持久性,LevelDB必须引入一个额外的持久化文件:预写日志(Write-Ahead Log,WAL),LevelDB中所有的写操作都必须先成功 append 到日志 WALLog,然后再更新到 MemTable ,将 MemTable 成功写入 SSTable 之后,然后再删除相应的预写日志。这样做的目的:
- 可以将随机写IO变成顺序追加IO,极大的提高写请求处理速度。
- 防止服务宕机导致内存数据丢失,恢复时可以从log中恢复尚未持久化到磁盘的数据。
WALog 格式
定义在 log_format.h
enum RecordType
{
// Zero is reserved for preallocated files
kZeroType = 0,
kFullType = 1,
// For fragments
kFirstType = 2,
kMiddleType = 3,
kLastType = 4
};
static const int kMaxRecordType = kLastType;
static const int kBlockSize = 32768;
// header 格式形如:
//struct header {
// uint32_t crc_; // 校验
// uint8_t length_low_; // 长度的低8位
// uint8_t length_high_; // 长度的高8位
// uint8_t type_; // 数据的类型
//};
static const int kHeaderSize = 4 + 2 + 1;
// Record 格式形如:
//struct Record {
// unsigned char header_[7];
// slice data_;
//};
WAL 文件以块为单位,每一个块大小为 32768 字节,一条记录可能全部写到一个块上,也可能跨几个块。
单条记录 + 对应头部格式如图:
![](https://img.haomeiwen.com/i7304940/b2b4a05248a10d7a.png)
每一个block至少由1个头部(header)与内容(content)组成,头部(header)由4字节校验,2字节长度,1字节类型共7字节组成。
理解 RecordType
一个block的大小是32KB = 32768,由于record
的大小并不是固定的(这里的record我们定义为上层来的一次写入):
-
1个record 足够放入1个block
换言之,一个block上面承载了多个record:
image.png
那么 type 就是kFullType
-
1个record 需要跨越3个及其以上的block
按照上面的规则:每一个block至少由1个头部(header)与内容(content)组成,所以record会被打散在3个block中。image.png
那么这3个Block的header类型分别是
kFirstType
kMiddleType
kLastType
网友评论