1. 程式人生 > >leveldb官方手冊摘錄

leveldb官方手冊摘錄

本文內容摘自leveldb官方手冊,版權歸其所有

CHAPTER 1 基本概念

leveldb是一個寫效能十分優秀的儲存引擎,是典型的LSM樹(Log Structured-Merge Tree)實現。LSM樹的核心
思想就是放棄部分讀的效能,換取最大的寫入能力

LSM樹寫效能極高的原理,簡單地來說就是儘量減少隨機寫的次數

對於每次寫入操作,並不是直接將最新的資料駐留在磁碟中,而是將其拆分成
(1)一次日誌檔案的順序寫
(2)一次記憶體中的資料插入

leveldb正是實踐了這種思想,將資料首先更新在記憶體中,當記憶體中的資料達到一定的閾值,將這部分資料真正重新整理到磁碟檔案中,因而獲得了極高的寫效能(順序寫60MB/s, 隨機寫45MB/s)。

1.1 整體架構

leveldb中主要由以下幾個重要的部件構成:

  • memtable
  • immutable memtable
  • log(journal)
  • sstable
  • manifest
  • current

1.1.1 memtable

memtable就是一個在記憶體中進行資料組織與維護的結構。

memtable中,所有的資料按使用者定義的排序
方法
排序之後按序儲存,等到其儲存內容的容量達到閾值時(預設為4MB),便將其轉換成一個不可修改
的memtable
,與此同時建立一個新的memtable,供使用者繼續進行讀寫操作。

memtable

底層使用了一種跳錶
據結構,這種資料結構效率可以比擬二叉查詢樹,絕大多數操作的時間複雜度為O(log n)

在這裡插入圖片描述

1.1.2 immutable memtable

memtable的容量到達閾值時,便會轉換成一個不可修改的memtable,也稱為immutable memtable

這兩者的結構定義完全一樣,區別只是immutable memtable是隻讀的。

當一個immutable memtable被建立時,leveldb
後臺壓縮排程便會將利用其中的內容,建立一個sstable持久化到磁碟檔案中。

1.1.3 log

leveldb在寫記憶體之前會首先將所有的寫操作寫到日誌檔案中,也就是log檔案。

當以下異常情況發生時,均可以通過日誌檔案進行恢復

  1. 寫log期間程序異常;
  2. log完成,寫記憶體未完成;
  3. write動作完成(即log、記憶體寫入都完成)後,程序異常;
  4. Immutable memtable持久化過程中程序異常;
  5. 其他壓縮異常(較為複雜,首先不在這裡介紹);

第一類情況發生時,資料庫重啟讀取log時,發現異常日誌資料,拋棄該條日誌資料,即視作這次使用者寫入失敗,保障資料庫的一致性
第二類,第三類,第四類情況發生了,均可以通過redo日誌檔案中記錄的寫入操作完成資料庫的恢復。

每次日誌的寫操作都是一次順序寫,因此寫效率高,整體寫入效能較好。
此外,leveldb的使用者寫操作的原子性同樣通過日誌來實現。

1.1.4 sstable

除了某些元資料檔案,leveldb的資料主要都是通過sstable來進行儲存

雖然在記憶體中,所有的資料都是按序排列的,但是當多個memetable資料持久化到磁碟後,對應的不同的sstable之間是存在交集的,在讀操作時,需要對所有的sstable檔案進行遍歷,嚴重影響了讀取效率。

因此leveldb後臺會**“定期“整合這些sstable檔案,該過程也稱為compaction**。

隨著compaction的進行,sstable檔案在邏輯上被分成若干層,由記憶體資料直接dump出來的檔案稱為level 0層檔案,後期整合而成的檔案為level i層檔案,這也是leveldb這個名字的由來

1.1.5 manifest

leveldb中有個版本的概念,一個版本中主要記錄了每一層中所有檔案的元資料

元資料包括
(1)檔案大小
(2)最大key值
(3)最小key值

該版本資訊十分關鍵,除了在查詢資料時,利用維護的每個檔案的最大/小key值來加快查詢,還在其中維護了一些進行compaction統計值,來控制compaction的進行

goleveldb為例,一個檔案的元資料主要包括了最大最小key,檔案大小等資訊;

// tFile holds basic information about a table.
type tFile struct 
{
	fd storage.FileDesc
	seekLeft int32
	size int64
	imin, imax internalKey
}

一個版本資訊主要維護了每一層所有檔案的元資料

type version struct 
{
	s *session 			// session - version
	
	levels []tFiles 	// file meta
	
	// Level that should be compacted next and its compaction score.
	// Score < 1 means compaction is not strictly needed. These fields
	// are initialized by computeCompaction()
	cLevel int 		// next level
	cScore float64 	// current score
	
	cSeek unsafe.Pointer
	
	closing bool
	ref int
	released bool
}

當每次compaction完成(或者換一種更容易理解的說法,當每次sstable檔案有新增或者減少),leveldb都會建立一個新的version

建立的規則是:

versionNew = versionOld + versionEdit

versionEdit指代的是基於舊版本的基礎上,變化的內容(例如新增或刪除了某些sstable檔案)

manifest檔案就是用來記錄這些versionEdit資訊的。

一個versionEdit資料,會被編碼成一條記錄,寫入manifest檔案中。

例如下圖便是一個manifest檔案的示意圖,其中包含了3條versionEdit記錄,
每條記錄包括
(1)新增哪些sst檔案
(2)刪除哪些sst檔案
(3)當前compaction下標
(4)日誌檔案編號
(5)操作seqNumber等資訊

通過這些資訊,leveldb便可以在啟動時,基於一個空的version,不斷apply這些記錄,最終得到一個上次執行結束時的版本資訊
在這裡插入圖片描述

1.1.6 current

這個檔案的內容只有一個資訊,就是記載當前的manifest檔名。

因為每次leveldb啟動時,都會建立一個新的Manifest檔案。因此資料目錄可能會存在多個Manifest檔案。

Current則用來指出哪個Manifest檔案才是我們關心的那個Manifest檔案