1. 日志格式
参考 https://zhuanlan.zhihu.com/p/149794318
1.1 每条记录Record有7B存储meta信息
1.2 log按照32KB切分,如果32KB还剩下小于7B, 由于Record只能存放meta信息,所以填'\x00' * leftover
if (leftover < kHeaderSize) {
// Switch to a new block
if (leftover > 0) {
// Fill the trailer (literal below relies on kHeaderSize being 7)
static_assert(kHeaderSize == 7, "");
dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover));
}
block_offset_ = 0;
}
1.3 record和block的关系(FULL, FIRST, MIDDLE, LAST)
(1) A 在一个Block中完整存在;B一部分在Block1, 中间部分在Block2(显然B超过32K才有这种情况), B最后一部分在Block3
2.代码细节
2.1 每条Recored7B元数据
- 7B的元数据在这里完成组装,注释中payload 是指信息传输中的有用数据;
- 默认是写一条Record语义执行Flush到内核缓冲区,LogWriter本身不保证数据落盘; sycn语义在外部调用时用sync参数指定,才能保证每次写数据落盘。
Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr,
size_t length) {
// Format the header
char buf[kHeaderSize];
buf[4] = static_cast<char>(length & 0xff);
buf[5] = static_cast<char>(length >> 8);
buf[6] = static_cast<char>(t);
// Compute the crc of the record type and the payload.
uint32_t crc = crc32c::Extend(type_crc_[t], ptr, length);
crc = crc32c::Mask(crc); // Adjust for storage
EncodeFixed32(buf, crc);
// Write the header and the payload
Status s = dest_->Append(Slice(buf, kHeaderSize));
if (s.ok()) {
s = dest_->Append(Slice(ptr, length));
if (s.ok()) {
s = dest_->Flush();
}
}
block_offset_ += kHeaderSize + length;
return s;
}
2.2 【高性能】 log_writer.cc 中 log::Writer的成员 uint32_t type_crc_[kMaxRecordType + 1]; 每次不需要计算LogType的CRC
static void InitTypeCrc(uint32_t* type_crc) {
for(int i = 0; i <= kMaxRecordType; i++) {
char t = i;
type_crc[i] = crc32c::Value(&t, 1);
}
}
uint32_t crc = crc32c::Extend(type_crc_[t], ptr, length);
本节代码地址
section 5, log_writer · 9DemonFox/myleveldb@72728f1 · GitHub