leveldb研究系列三——log文件系统

从这一章节我开始讲具体组件和源代码,  阅读源代码的工具很多,leveldb的源代码不算大加上测试代码不过一万五千多行,一个比较好的源代码阅读工具是source insight。  不过要把文件名后缀.cc改成.cpp 建好一个项目 开始我们的征程。

 首先我们讲log文件,write的第一站,也是系统容灾的常见方法。

log文件结构很简单,write依次将<key,value>按照log的format格式化写入   appand写入这里不存在随机i/O。

log文件的格式说明在/doc/log_format.txt下有具体描述

The log file contents are a sequence of 32KB blocks.  The only exception is that the tail of the file may contain a partial block.

Each block consists of a sequence of records: block := record* trailer?
   record := checksum: uint32 // crc32c of type and data[] ; little-endian
length: uint16 // little-endian
type: uint8 // One of FULL, FIRST, MIDDLE, LAST
data: uint8[length] 

这里重点讲一下type类型,在 Log_format.h中

enum RecordType {
  // Zero is reserved for preallocated files
  kZeroType = 0,

  kFullType = 1,

  // For fragments
  kFirstType = 2,
  kMiddleType = 3,
  kLastType = 4
};
record是连续存储的,磁盘Block可能截断record  。Type用来标记record和当前Block的关系
KzeroType标识预留未被使用的BLock; 

kFulltype表示此record所有部分都在当前Block下,

KFirstType表示此record的部分数据在当前block下,剩余数据在下一Block中(或者在下面连续几个Block中)

KMiddleType 标识此record的数据部分不仅仅全部占有此Block,而且在上一个和下一个Block也有数据

KlastType标识此record的数据部分在前一个Block(或者前几个连续Block中还有数据)




下面给出写log的操作,在Log_writer.cpp中 AddRecord函数是写日志的功能,实际写入磁盘,内部调用了EmitPhysicalRecord()函数,并且把CRC校验写入,这里面数据存储是小端形式

Status Writer::AddRecord(const Slice& slice) {
  const char* ptr = slice.data();
  size_t left = slice.size();

  // Fragment the record if necessary and emit it.  Note that if slice
  // is empty, we still want to iterate once to emit a single
  // zero-length record
  Status s;
  bool begin = true;
  do {
    const int leftover = kBlockSize - block_offset_;  //计算剩余容量
    assert(leftover >= 0);
    if (leftover < kHeaderSize) {
      // Switch to a new block
      if (leftover > 0) {
        // Fill the trailer (literal below relies on kHeaderSize being 7)
        assert(kHeaderSize == 7);
        dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover)); //如果容量小于record的数据头,record的最小值则
                                                                    //填充0
      }
      block_offset_ = 0;
    }

    // Invariant: we never leave < kHeaderSize bytes in a block.
    assert(kBlockSize - block_offset_ - kHeaderSize >= 0);

    const size_t avail = kBlockSize - block_offset_ - kHeaderSize;
    const size_t fragment_length = (left < avail) ? left : avail; //计算block剩余大小,以及本次log record可写入数据长度

    RecordType type;
    const bool end = (left == fragment_length);   //end==true表示剩余大小为0,磁盘Block写满
    if (begin && end) {
      type = kFullType;
    } else if (begin) {
      type = kFirstType;
    } else if (end) {
      type = kLastType;
    } else {
      type = kMiddleType;      
    }

    s = EmitPhysicalRecord(type, ptr, fragment_length);  //这里写入磁盘 填入CRC校验
    ptr += fragment_length;
    left -= fragment_length;
    begin = false;                //下一block标记begin==false,表示未完待续哦。。。
  } while (s.ok() && left > 0);   //   do..while结束
  return s;
}

Log写很简单,读复杂的多,不过只有在数据库灾害恢复时候才会去读Log,精力有限没有去研究读操作。哈哈

刚刚也说的数据存储的大小端问题  leveldb才用小端模式

void EncodeFixed32(char* buf, uint32_t value) {
  if (port::kLittleEndian) {
    memcpy(buf, &value, sizeof(value));
  } else {
    buf[0] = value & 0xff;
    buf[1] = (value >> 8) & 0xff;
    buf[2] = (value >> 16) & 0xff;
    buf[3] = (value >> 24) & 0xff;
  }
}

这里我只想说位移运算"<<"  是算术位移,不能看做物理位移,否则大端的位移就是物理左移才能取到int的低位数(低位数在高字节)   这一点希望能够理解。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值