rockdb 源码解析:Log文件结构 (仅供参考)

       
                     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




猜你喜欢

转载自blog.csdn.net/dongfengxueli/article/details/64929844