leveldb源碼分析--日誌

咱們知道在一個數據庫系統中爲了保證數據的可靠性,咱們都會記錄對系統的操做日誌。日誌的功能就是用來在系統down掉的時候對數據進行恢復,因此日誌系統對一個要求可靠性的存儲系統是極其重要的。接下來咱們分析leveldb的日誌,首先在leveldb源碼目錄中有doc/log_format.txt,這個文件詳細的描述了leveldb的日誌格式:數據庫

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]

以下圖(圖片引用):學習

leveldb在寫日誌時,對日誌文件進行了劃分爲多個32K的文件塊,每次讀寫日誌時都以這樣的每一個32K爲單位。這樣進行處理之後leveldb的日誌文件的大體組成就能夠看作爲以下的形式:ui

那麼咱們初步描述一下Log的寫入狀況爲:spa

當要寫入一條首先判斷當前block中是否足夠存放該條日誌日誌

      S1.若是足夠那麼直接安裝格式寫入;code

      S2.若是不夠那麼計算出去頭之外能夠存放多少內容,將內容組裝爲FIRST的Log typpe寫入;而後新取一個塊判斷是否足夠存放剩下的日誌數據orm

       while(數據未寫完)blog

            S21. 若是足夠就組裝爲LAST的形式寫入;圖片

            S22. 若是仍然不夠就組裝爲MIDDLE的形式寫入源碼

因此就容易理解這裏的FULL,FIRST,MIDDLE和LAST了:

    FULL:一條完整的日誌被寫到block

    FIRST:一條日誌,可是當前block沒法徹底寫入,有部分數據被寫到了下一個block,當前block的數據只是日誌的開始(第一)部分

    MIDDLE:該日誌內容是接着前一個block裏面的最後一條日誌的繼續,並且本block還沒法徹底寫完,在下一個block中繼續有該條日誌的數據

    LAST:    以前block的未寫完的日誌的最後一部分;

另外須要注意的就是根據前面的描述咱們能夠想象獲得一個block在寫入一部分數據之後會剩下部分空間,這個空間多是大於7byte,等於7byte,小於7byte;這裏爲何要以7byte爲分界呢?日誌記錄的header(crc|length|type)長度爲7,若是超過7就至少能夠存一個FISRT的部分日誌記錄,而等於7就恰好存一個header,少於7就連header都存不了。LOG也正是基於這樣的緣由,小於7時就補充」\0」,7就存一個空header。咱們來看看代碼邏輯

Status Writer::AddRecord(const Slice& slice) {
bool begin = true;
// 循環向日志文件寫,直到寫完爲止
do {
    const int leftover = kBlockSize - block_offset_;
    if (leftover < kHeaderSize) {// 小於 7 byte (header size )填充 0x0 
      if (leftover > 0) {
        dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover));
      }
      block_offset_ = 0;
    }

const size_t avail = kBlockSize - block_offset_ - kHeaderSize;
    const size_t fragment_length = (left < avail) ? left : avail;

    RecordType type;
    const bool end = (left == fragment_length);{// 判斷本段可否寫完
    if (begin && end) {     // 開始結束都在本block
      type = kFullType;
    } else if (begin) {     // 開始在本block,結束不在
      type = kFirstType;
    } else if (end) {       // 結束在,開始不在
      type = kLastType;
    } else {                // 開始結束都不在本block
      type = kMiddleType;
    }
 // encode 而後再寫入文件中 
    s = EmitPhysicalRecord(type, ptr, fragment_length);
    ptr += fragment_length;
    left -= fragment_length;
    begin = false;
  } while (s.ok() && left > 0);
  return s;
}

讀日誌的代碼在log_reader.cc中,代碼的邏輯比寫負責不少,主要是讀入時會須要增長不少錯誤處理相關的內容,具體的代碼不在羅列,理解了日誌文件的格式之後的很容易就能讀懂。固然同時其異常處理的邏輯也是咱們碼農們學習的材料,理解一下高手們是如何進行各類錯誤處理的。

相關文章
相關標籤/搜索