庖丁解LevelDB之?dāng)?shù)據(jù)存儲(chǔ)

作為一個(gè)存儲(chǔ)引擎步鉴,數(shù)據(jù)存儲(chǔ)自然是LevelDB重中之重的需求。我們已經(jīng)在庖丁解LevelDB之概覽中介紹了Leveldb的使用流程,以及數(shù)據(jù)在Memtable泉孩,Immutable,SST文件之間的流動(dòng)眨补。本文就將詳細(xì)的介紹LevelDB的數(shù)據(jù)存儲(chǔ)方式,包括數(shù)據(jù)在不同介質(zhì)中的存儲(chǔ)方式旗笔,數(shù)據(jù)結(jié)構(gòu)及設(shè)計(jì)思路。

Memtable

Memtable對(duì)應(yīng)Leveldb中的內(nèi)存數(shù)據(jù)离例,LevelDB的寫入操作會(huì)直接將數(shù)據(jù)寫入到Memtable后返回换团。讀取操作又會(huì)首先嘗試從Memtable中進(jìn)行查詢。我們對(duì)Memtable的需求如下:

  • 常駐內(nèi)存宫蛆;
  • 可能會(huì)有頻繁的插入和查詢操作艘包;
  • 不會(huì)有刪除操作;
  • 需要支持阻寫狀態(tài)下的遍歷操作(Immutable的Dump過程)

LevelDB采用跳表SkipList實(shí)現(xiàn)耀盗,在給提供了O(logn)的時(shí)間復(fù)雜度的同時(shí)想虎,又非常的易于實(shí)現(xiàn):

跳表

SkipList中單條數(shù)據(jù)存放一條Key-Value數(shù)據(jù),定義為:

SkipList Node := InternalKey + ValueString
InternalKey := KeyString + SequenceNum + Type
Type := kDelete or kValue
ValueString := ValueLength + Value
KeyString := UserKeyLength + UserKey

Log

數(shù)據(jù)寫入Memtable之前叛拷,會(huì)首先順序?qū)懭隠og文件舌厨,以避免數(shù)據(jù)丟失。LevelDB實(shí)例啟動(dòng)時(shí)會(huì)從Log文件中恢復(fù)Memtable內(nèi)容忿薇。所以我們對(duì)Log的需求是:

  • 磁盤存儲(chǔ)
  • 大量的Append操作
  • 沒有刪除單條數(shù)據(jù)的操作
  • 遍歷的讀操作

LevelDB首先將每條寫入數(shù)據(jù)序列化為一個(gè)Record裙椭,單個(gè)Log文件中包含多個(gè)Record。同時(shí)署浩,Log文件又劃分為固定大小的Block單位揉燃,并保證Block的開始位置一定是一個(gè)新的Record。這種安排使得發(fā)生數(shù)據(jù)錯(cuò)誤時(shí)筋栋,最多只需丟棄一個(gè)Block大小的內(nèi)容炊汤。顯而易見地,不同的Record可能共存于一個(gè)Block弊攘,同時(shí)抢腐,一個(gè)Record也可能橫跨幾個(gè)Block。

Log format
Block := Record * N
Record := Header + Content
Header := Checksum + Length + Type
Type := Full or First or Midder or Last

Log文件劃分為固定長度的Block襟交,每個(gè)Block中包含多個(gè)Record迈倍;Record的前56個(gè)字節(jié)為Record頭,包括32位checksum用做校驗(yàn)捣域,16位存儲(chǔ)Record實(shí)際內(nèi)容數(shù)據(jù)的長度授瘦,8位的Type可以是Full、First竟宋、Middle或Last中的一種提完,表示該Record是否完整的在當(dāng)前的Block中,如果不是則通過Type指明其前后的Block中是否有當(dāng)前Record的前驅(qū)后繼丘侠。

SST文件

SST文件是Leveldb中數(shù)據(jù)的最終存儲(chǔ)角色徒欣,劃分為不同的Level,Level 0的SST文件由Memtable直接Dump產(chǎn)生蜗字。其他層次的SST文件則由其上一層文件在Compaction過程中歸并產(chǎn)生打肝。讀請(qǐng)求時(shí)可能會(huì)從SST文件中查找某條數(shù)據(jù)脂新。我們對(duì)SST文件的需求是:

  • 支持順序?qū)懖僮?/li>
  • 支持遍歷操作
  • 查找操作

我們將從物理格式和邏輯格式兩個(gè)方面來介紹SST文件中的數(shù)據(jù)存儲(chǔ)方式。所謂物理格式指的是數(shù)據(jù)的存儲(chǔ)和解析方式粗梭;利用確定的物理格式争便,我們可以存儲(chǔ)不同意義的數(shù)據(jù),這就是數(shù)據(jù)的邏輯格式断医。

物理格式

LevelDB將SST文件定義為Table滞乙,每個(gè)Table又劃分為多個(gè)連續(xù)的Block,每個(gè)Block中又存儲(chǔ)多條數(shù)據(jù)Entry:

SST物理格式

可以看出鉴嗤,單個(gè)Block作為一個(gè)獨(dú)立的寫入和解析單位斩启,會(huì)在其末尾存儲(chǔ)一個(gè)字節(jié)的Type和4個(gè)字節(jié)的Crc,其中Type記錄的是當(dāng)前Block的數(shù)據(jù)壓縮策略醉锅,而Crc則存儲(chǔ)Block中數(shù)據(jù)的校驗(yàn)信息兔簇。Block中每條數(shù)據(jù)Entry是以Key-Value方式存儲(chǔ)的,并且是按Key有序存儲(chǔ)硬耍,Leveldb很巧妙了利用了有序數(shù)組相鄰Key可能有相同的Prefix的特點(diǎn)來減少存儲(chǔ)數(shù)據(jù)量垄琐。如上圖所示,每個(gè)Entry只記錄自己的Key與前一個(gè)Entry Key的不同部分经柴,例如要順序存儲(chǔ)Key值“apple”和“applepen”的兩條數(shù)據(jù)狸窘,這里第二個(gè)Entry中只需要存儲(chǔ)“pen”的信息。在Entry開頭記錄三個(gè)長度值口锭,分別是當(dāng)前Entry和其之前Entry的公共Key Prefix長度、當(dāng)前Entry Key自有Key部分的長度和Value的長度介杆。通過這些長度信息和其后相鄰的特有Key及Value內(nèi)容鹃操,結(jié)合前一條Entry的Key內(nèi)容,我們可以方便的獲得當(dāng)前Entry的完整Key和Value信息春哨。

這種方式非常好的減少了數(shù)據(jù)存儲(chǔ)荆隘,但同時(shí)也引入一個(gè)風(fēng)險(xiǎn),如果最開頭的Entry數(shù)據(jù)損壞赴背,其后的所有Entry都將無法恢復(fù)椰拒。為了降低這個(gè)風(fēng)險(xiǎn),leveldb引入了重啟點(diǎn)凰荚,每隔固定條數(shù)Entry會(huì)強(qiáng)制加入一個(gè)重啟點(diǎn)燃观,這個(gè)位置的Entry會(huì)完整的記錄自己的Key,并將其shared值設(shè)置為0便瑟。同時(shí)缆毁,Block會(huì)將這些重啟點(diǎn)的偏移量及個(gè)數(shù)記錄在所有Entry后邊的Tailer中。

邏輯格式

Table中不同的Block物理上的存儲(chǔ)方式一致到涂,如上文所示脊框,但在邏輯上可能存儲(chǔ)不同的內(nèi)容颁督,包括存儲(chǔ)數(shù)據(jù)的Block,存儲(chǔ)索引信息的Block浇雹,存儲(chǔ)Filter的Block:

SST邏輯格式
  • Footer:為于Table尾部沉御,記錄指向Metaindex Block的Handle和指向Index Block的Handle。需要說明的是Table中所有的Handle是通過偏移量Offset以及Size一同來表示的昭灵,用來指明所指向的Block位置吠裆。Footer是SST文件解析開始的地方,通過Footer中記錄的這兩個(gè)關(guān)鍵元信息Block的位置虎锚,可以方便的開啟之后的解析工作硫痰。另外Footer種還記錄了用于驗(yàn)證文件是否為合法SST文件的常數(shù)值Magicnum。

  • Index Block:記錄Data Block位置信息的Block窜护,其中的每一條Entry指向一個(gè)Data Block效斑,其Key值為所指向的Data Block最后一條數(shù)據(jù)的Key,Value為指向該Data Block位置的Handle?柱徙。

  • Metaindex Block:與Index Block類似缓屠,由一組Handle組成,不同的是這里的Handle指向的Meta Block护侮。

  • Data Block:以Key-Value的方式存儲(chǔ)實(shí)際數(shù)據(jù)敌完,其中Key定義為:

    DataBlock Key := UserKey + SequenceNum + Type
    Type := kDelete or kValue
    

    對(duì)比Memtable中的Key,可以發(fā)現(xiàn)Data Block中的Key并沒有拼接UserKey的長度在UserKey前羊初,這是由于上面講到的物理結(jié)構(gòu)中已經(jīng)有了Key的長度信息滨溉。

  • Meta Block:比較特殊的Block,用來存儲(chǔ)元信息长赞,目前LevelDB使用的僅有對(duì)布隆過濾器的存儲(chǔ)晦攒。寫入Data Block的數(shù)據(jù)會(huì)同時(shí)更新對(duì)應(yīng)Meta Block中的過濾器。讀取數(shù)據(jù)時(shí)也會(huì)首先經(jīng)過布隆過濾器過濾得哆。Meta Block的物理結(jié)構(gòu)也與其他Block有所不同:

     [filter 0]
     [filter 1] 
     [filter 2] 
     ... 
     [filter N-1] 
     [offset of filter 0] : 4 bytes 
     [offset of filter 1] : 4 bytes 
     [offset of filter 2] : 4 bytes 
     ... 
     [offset of filter N-1] : 4 bytes 
     [offset of beginning of offset array] : 4 bytes 
     lg(base) : 1 byte
    

    其中每個(gè)filter節(jié)對(duì)應(yīng)一段Key Range脯颜,落在某個(gè)Key Range的Key需要到對(duì)應(yīng)的filter節(jié)中查找自己的過濾信息,base指定這個(gè)Range的大小贩据。

總結(jié)

本文重點(diǎn)介紹了LevelDB不同介質(zhì)角色中數(shù)據(jù)的存儲(chǔ)方式栋操,并沒有過多的涉及代碼細(xì)節(jié)。因?yàn)橐坏┯辛司唧w的存儲(chǔ)格式饱亮,相關(guān)的代碼不過就是在讀寫兩端的序列化反序列化矾芙。同樣,之后幾篇博客將要介紹的版本控制近上,迭代器蠕啄,緩存等方面的內(nèi)容也都非常緊密的依賴這些存儲(chǔ)格式。

參考

Source Code:https://github.com/google/leveldb

LevelDB Doc: https://raw.githubusercontent.com/google/leveldb/master/doc/table_format.txt

LevelDB Doc: https://raw.githubusercontent.com/google/leveldb/master/doc/log_format.txt

庖丁解LevelDB之概覽: http://catkang.github.io/2017/01/07/leveldb-summary.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市歼跟,隨后出現(xiàn)的幾起案子和媳,更是在濱河造成了極大的恐慌,老刑警劉巖哈街,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件留瞳,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡骚秦,警方通過查閱死者的電腦和手機(jī)她倘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來作箍,“玉大人硬梁,你說我怎么就攤上這事“茫” “怎么了荧止?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長阶剑。 經(jīng)常有香客問我跃巡,道長,這世上最難降的妖魔是什么牧愁? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任素邪,我火速辦了婚禮,結(jié)果婚禮上猪半,老公的妹妹穿的比我還像新娘兔朦。我一直安慰自己,他們只是感情好磨确,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布沽甥。 她就那樣靜靜地躺著,像睡著了一般俐填。 火紅的嫁衣襯著肌膚如雪安接。 梳的紋絲不亂的頭發(fā)上翔忽,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天英融,我揣著相機(jī)與錄音,去河邊找鬼歇式。 笑死驶悟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的材失。 我是一名探鬼主播痕鳍,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了笼呆?” 一聲冷哼從身側(cè)響起熊响,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诗赌,沒想到半個(gè)月后汗茄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铭若,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年洪碳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叼屠。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瞳腌,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出镜雨,到底是詐尸還是另有隱情嫂侍,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布冷离,位于F島的核電站吵冒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏西剥。R本人自食惡果不足惜痹栖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望瞭空。 院中可真熱鬧揪阿,春花似錦、人聲如沸咆畏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旧找。三九已至溺健,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钮蛛,已是汗流浹背鞭缭。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留魏颓,地道東北人岭辣。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像甸饱,于是被迫代替她去往敵國和親沦童。 傳聞我的和親對(duì)象是個(gè)殘疾皇子仑濒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容