Log文件结构
Log文件格式:
Blocksize=32kb
rn :代表不同长度的
record
P:代表填充,当一个
block
剩余的空间不足以放下一个新的
record
时,剩下的空间会用
\0
填充,空的
record
最短长度为
7B
,当剩余空间恰好为
7B
时,
Writer
发出一个
first record
填满剩余空间,并告知所有的用户数据在下一个新的
block
Kheadersize=4+2+1=7,空
record
最短为
7B
Record记录格式:
CRC:
32
位是有效负载的校验值
Size:
16
位,有效数据的长度
Type:
record
的类型,
Kzerotype=0:为预配置文件的保留类型
kFullType=1:表示
record
记录是完整的
kFirstType=2:
record
被拆分存储的第一个碎片
kMiddleType=3:
record
被拆分存储的其余碎片
kLastType=4:
record
被拆分存储的第一个碎片
Payload:有效数据,大小由
size
可知
Log的写入情况为:
当要写入一条首先判断当前block中是否足够存放该条日志
S1.如果足够那么直接安装格式写入;
S2.如果不够那么计算出去头以外可以存放多少内容,将内容组装为FIRST的Log typpe写入;然后新取一个块判断是否足够存放剩下的日志数据
while(数据未写完)
S21. 如果足够就组装为LAST的形式写入;
S22. 如果仍然不够就组装为MIDDLE的形式写入
Write写
record
流程:
Log文件写入是通过
Writer
类添加数据到
WritableFileWrite
类指针
*dest
,
dest_->Append()
函数实现
Log文件会先缓存到
LogBuffer
类,最后会被
flush
到
log
文件中去
Reader读
record
流程:
首先调用SkipToInitialBlock()来找到记录的起始位置,然后调用
ReadPhysicalRecord
()函数,
ReadPhysicalRecord
()函数做的是解析一条条
record
,得到
type
大小和检验
crc
,返回值是
record_type
类型,再根据
record_type
来将
record
数据临时缓存到
*scratch
字符串中,然后将整理得到的
record
保存到
Slice
类
fragment
,详细流程见流程图
Reader类
namespace rocksdb {
class SequentialFileReader;
using std::unique_ptr; //全局指针
namespace log {
//包含Reader类,实际读由SequentialFile 接口完成
class Reader {
public:
class Reporter {
public:
virtual ~Reporter(); //虚拟函数实现覆盖
virtual void Corruption(size_t bytes, const Status& status) = 0; //size 是碰撞大小字节
};
Reader(unique_ptr<SequentialFileReader>&& file, Reporter* reporter,
bool checksum, uint64_t initial_offset);
// "*file" must remain live while this Reader is in use.
//新建一个Reader类,会从 *file指针 返回log 记录
// "*reporter" must remain live while this Reader is in use.
//Reader会从第一个record开始,物理位置为initial_offset开始
~Reader();
bool ReadRecord(Slice* record, std::string* scratch,
bool report_eof_inconsistency = false);
//下一条记录会写到*record指针中,如果成功返回true,如果到输入尾部返回false
//使用*scratch作为临时存储
uint64_t LastRecordOffset(); //返回last record的物理偏移量
bool IsEOF() { //判断reader是否到eof
return eof_;
}
void UnmarkEOF(); //当我们知道有更多的data被写到文件中,我们可以用这个函数来强制reader重新搜索文件
SequentialFileReader* file() { return file_.get(); }
private:
const unique_ptr<SequentialFileReader> file_;
Reporter* const reporter_;
bool const checksum_;
char* const backing_store_;
Slice buffer_;
bool eof_;
bool read_error_;
size_t eof_offset_;
uint64_t last_record_offset_; //ReadRecord 返回最后记录的偏移
uint64_t end_of_buffer_offset_; //buffer结束 的第一个位置偏移
uint64_t const initial_offset_; //最开始查询record的记录偏移
enum { //使用以下特殊值来记录扩展类型
kEof = kMaxRecordType + 1, //扩展类型,KMaxRecordType=4
// Currently there are three situations in which this happens:
// * The record has an invalid CRC (ReadPhysicalRecord reports a drop)
// * The record is a 0-length record (No drop is reported)
// * The record is below constructor's initial_offset (No drop is reported)
kBadRecord = kMaxRecordType + 2
};
bool SkipToInitialBlock(); //跳到record起始位置,成功返回true
unsigned int ReadPhysicalRecord(Slice* result,
bool report_eof_inconsistency = false); //返回type或者前面的special values
// Reprts dropped bytes to the reporter.
// buffer_ must be updated to remove the dropped bytes prior to invocation.
void ReportCorruption(size_t bytes, const char* reason);
void ReportDrop(size_t bytes, const Status& reason);
// No copying allowed
Reader(const Reader&);
void operator=(const Reader&);
};
} // namespace log
Write类
namespace rocksdb {
class WritableFileWriter;
using std::unique_ptr;
namespace log {
/**write是一个通用的日志流文件,提供一个只能追加写的方式,写数据的细节由WriteableFile 子类完成
*
*file分解成变长大小records,record格式如下:
* +-----+-------------+--+----+----------+------+-- ... ----+
* File | r0 | r1 | P | r2 | r3 | r4 | |
* +-----+-------------+--+----+----------+------+-- ... ----+
* <--- kBlockSize ------>|<-- kBlockSize ------>|
* rn = variable size records
* P = Padding
*
data 写在kBlcokSize块里,下条record不符合剩余空间,将会被 \0填充
* Record format:
*
* +---------+-----------+-----------+--- ... ---+
* |CRC (4B) | Size (2B) | Type (1B) | Payload |
* +---------+-----------+-----------+--- ... ---+
*
* CRC = 32位校验值
* Size = 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
*/
class Writer {
public:
//创建一个writer类,在*dest后添加数据,*dest初始必须为空,*dest必须live当Writer类使用时
explicit Writer(unique_ptr<WritableFileWriter>&& dest);
~Writer();
Status AddRecord(const Slice& slice);
WritableFileWriter* file() { return dest_.get(); }
const WritableFileWriter* file() const { return dest_.get(); }
private:
unique_ptr<WritableFileWriter> dest_;
int block_offset_; // 当前block内的偏移
uint32_t type_crc_[kMaxRecordType + 1]; //对于所有支持record type 的crc32c的值
Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length);
Writer(const Writer&); //不允许cooy
void operator=(const Writer&);
};
} // namespace log
} // namespace rocksdb
logBuffer类
class Logger; class LogBuffer { //logBuffer类是用来缓存log项,并且最后会被flush public: LogBuffer(const InfoLogLevel log_level, Logger* info_log); //log_level:log的层次 //info_log:logger 类 写log地方 void AddLogToBuffer(size_t max_log_size, const char* format, va_list ap); //在buffer缓冲区添加一个log项,默认最大log_size size_t IsEmpty() const { return logs_.empty(); } void FlushBufferToLog(); //buffer缓存写入到 info 日志中,并且清理buffer缓存 private: struct BufferedLog { struct timeval now_tv; //log时间戳 char message[1]; // log开始信息 }; const InfoLogLevel log_level_; Logger* info_log_; Arena arena_; autovector<BufferedLog*> logs_; }; extern void LogToBuffer(LogBuffer* log_buffer, size_t max_log_size, const char* format, ...); //调用AddLogToBuffer(),将log缓存到buffer区中,延迟添加到info log中,想要一些日志存在互斥 extern void LogToBuffer(LogBuffer* log_buffer, const char* format, ...); //上一个函数的重载,默认的最大日志大小 } // namespace rocksdb