咱們知道在一個數據庫系統中爲了保證數據的可靠性,咱們都會記錄對系統的操做日誌。日誌的功能就是用來在系統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中,代碼的邏輯比寫負責不少,主要是讀入時會須要增長不少錯誤處理相關的內容,具體的代碼不在羅列,理解了日誌文件的格式之後的很容易就能讀懂。固然同時其異常處理的邏輯也是咱們碼農們學習的材料,理解一下高手們是如何進行各類錯誤處理的。