LevelDB的源碼閱讀(一)
源碼下載
git clone https://github.com/google/leveldb.git
項目結構
- db/, 數據庫邏輯
- doc/, MD文檔
- helpers/, LevelDB內存版, 通過namespace覆蓋
- port/, 平臺相關代碼
- table/, LSM有關的
主要模塊
Log 文件
客戶端的寫請求會先 append 到 Log 文件,成功後再寫入到 Memtable。如果宕機可以通過 Log 文件來恢復 Memtable。
Memtable 和 Immutable Memtable
內存數據結構,基於跳表。客戶端的讀寫請求都會由 Memtable 處理。 當 Memtable 占用的內存達到一定閾值,重新生成新的 Memtable 處理客戶端請求。原來的 Memtable 轉成 Immutable Memtable,等待歸並到 SST 文件中。
SST 文件
落地到磁盤的存儲文件。SST 分為不同的 level,具體參考文檔。
Manifest 文件
Manifest 記錄不同 level 的 SST 文件,包括每個 SST 文件的 key range、大小等 metadata。
Current 文件
Current 記錄了最新的 Manifest 文件。
LSMtree的核心思想以及問題
在LSM Tree中,所有數據直接寫入memtable並打log, 當memtable足夠大的時候, 變為immemtable, 開始往硬盤挪, 成為SSTable. 你可以用任何有道理的數據結構來表示memtable, immemtable和SSTable. LevelDB選擇用跳躍表(skiplist)實現memtable和immemtable, 用有序行組來實現SSTable。
LSM Tree存在如下問題:
1.適用於插入多而查找少的情況。在查找key時,最壞情況要從memtable讀到immemtable, 再到所有SSTable.
2.SSTable要怎麽有效merge(major compaction)? 如果只有一個SSTable, 我要把新immemtable歸並進去, 就要重寫這個SSTable. 數據有多大, 這個SSTable也會有多大.那麽把SSTable分成若幹份, 每份2MB呢?在最壞的情況下,比如,當前這個immemtable恰好永遠有一個key與任意SSTable中至少一個key重復,就回到剛剛重寫全庫的情況了.
針對以上問題,LevelDB打了兩個增強補丁:
1. 添加BloomFilter, 這樣可以提升全庫掃描的速度, 直接跳過沒有這個key的SSTable.
2. leveled compaction, 把SSTable分成不同的等級. 除等級0以外, 其余各等級的SSTable不會有重復的key.
LevelDB的做法讓每次compaction波及到的範圍是可預期的. 官方文檔的說法是"The compaction picks a file from level L and all overlapping files from the next level L+1". 只按等級延遲合並,沒有任何隨機讀寫操作, 機制上簡單, 而且不需要bookkeeping,可以優雅得釋放被刪除記錄的空間。
需要註意的是:因為下級可能還有相同key的數據,因此,compaction不一定會清空所有deletion maker.
參考文獻:
1.https://zhuanlan.zhihu.com/p/27329248
2.http://masutangu.com/2017/06/leveldb_1/
LevelDB的源碼閱讀(一)