sstable檔案是memtable 資料到一定閾值寫入檔案形成的,由於記憶體容量總是有限的,將一定量資料寫入磁碟可以存放更多資料,所以leveldb相比redis能存放更多資料。既然資料持久化到磁碟,那麼還有必然涉及到從磁碟中查詢資料,從磁碟中查詢資料與從記憶體中查詢資料的效率是不一樣的,所以sstable 資料組織方式必然與眾不同,因為必須要提高查詢效率,不能給乙個key就去遍歷所有sstable。因此本文的另乙個目的就是學習sstable 檔案如何組織key-value,提高查詢效率。為了提高記憶體中資料查詢效率 我們學習了各種資料結構如紅黑樹,雜湊表,那麼sstable是學習如何提高檔案查詢資料效率的乙個很好例子。
在學習原始碼之前還是先看看sstable檔案的結構
整體上看 sstable檔案分為資料區與索引區,尾部的footer指出了meta index block與data index block的偏移與大小,index block指出了各data block的偏移與大小,metaindex block指出了filter block的偏移與大小。
1)data block:儲存key-value記錄,分為data、type、crc三部分
2)filter block:預設沒有使用,用於快速從data block 判斷key-value是否存在
3)metaindex block :記錄filter block的相關資訊
4)index block:描述乙個data block,儲存著對應data block的最大key值,以及data block在檔案中的偏移量和大小
5)footer:索引的索引,記錄metaindex block和index block在sstable中的偏移量了和大小
下面再具體看看各個部分物理結構
1、block
sstable中data block 、metaindex block、index block都用這種block這種結構。對於data block,當block大小(record、restarts陣列、以及num_restarts)超過4k時,就切換乙個新的block繼續往sstable寫資料,而metaindex block、index block就只有乙個block,所以上圖看起來data block有多個。
block主要由資料區record和restarts組成。
為什麼是這種結構?
data block主要是儲存資料,block內給一定數量(預設16)key-value分組,每組又用restarts陣列記錄起始位置,因此可以根據restarts讀取每組起始位置key-value,由於block內的資料是從小到大有序儲存的,所以可以通過restarts陣列,獲取每組起始key-value,比較起始key key(n)與查詢的key大小,如果key(n)>key,那麼key一定在序=n組之後,否則在 < n組之前。因此可以通過二分查詢思想通過restarts獲取起始key,來定位key的位置,避免線性查詢低效。
因此,restarts的思想就是提高block內key-value查詢效率,直接定位key所在group。
下面再來看看record結構。record相對有意思,不是簡單的用key-length | key-data | value-length|value-data儲存。
data block中的key是有序儲存的,相鄰的key之間可能有重複,因此儲存時採用字首壓縮,後乙個key只儲存與前乙個key不同的部分。重啟點指出的位置即每組起始位置的key不按字首壓縮,而是完整儲存該key。
type是表示資料是否壓縮,以怎樣的方式壓縮,crc32是該block校驗碼,這個非本文分析重點。
2、index block
index block 的結構也是block 結構,是data block的索引,記錄每個data block 最大key 和 起始位置以及大小。具體的儲存方式是以每個data block最大key 為key,以data block 起始位置和大小為value。因此可以根據每個block的最大key與查詢key比較,直接定位查詢key所在的位置。
這是理論上key的儲存方式,但是在sstable二次壓縮的過程對key做了乙個優化,它並不儲存最大key,而是儲存乙個能分隔兩個data block的最短key,如:假定data block1的最大乙個key為「abcdefg」,data block2最大key為「abzxcv」,則index可以記錄data block1的索引key為「abd」;這樣的分割串可以有很多,只要保證data block1中的所有key都小於等於此索引,data block2中的所有key都大於此索引即可。這種優化縮減了索引長度,查詢時可以有效減小比較次數。
因此,index block的思想是提高sstable內key-value查詢效率,直接定位key所在block。
3、metaindex_block
也是block結構。就只有一條記錄,其key是filter. + filter_policy的name,value是filter大小和起始位置。
4、filter block
filter block就是乙個bloom filter,關於bloom filter原理概念可以百度。
每個bloom filter是對data block 的key 經過hash num 次形成的位元組陣列,多少個data block對應多少個bloom filter。
bloom filter實質就是乙個bit 陣列,對block 內key hash,將相應的位置設為1,這種設計關鍵在於能提高不存在的key判斷效率,通過filter 計算,如果不存在,就不用通過data block內的restarts方式讀取檔案查詢key是否存在,但是如果filter判斷存在,還需通過restarts方式確定。
5、footer
footer位於sstable檔案尾部,占用空間固定為48個位元組。其末尾8個位元組是乙個magic_number。metaindex_block_handle與index_block_handle物理上占用了40個位元組,metaindex_block_handle和index_block_handle是blockhandle資料型別, 這種結構用於記錄metaindex block 和index block的起始位置和大小。
對於blockhandle ,其實可以看作檔案內容指標實現方式,blockhandle記錄資料位置及大小,與c/c++指標 思想類似,通過位址和大小可以讀取資料。
blockhandle格式
varint64 offset | varint64 size_
採用變長儲存,所以實際上儲存可能連32位元組都不到,剩餘填充0。
總結,sstable其實就是通過二次索引,先讀取footer,根據footer中index_block_handler記錄的index_block起始位置和大小,讀取index block,通過index block 查詢key所在data block,再在data block內部通過restarts 進一步確定key所在group。
下面是完整的sstable結構圖
leveldb原始碼分析之Arena
arena 是 leveldb 專案裡面使用的輕量級的記憶體池物件,leveldb 用這個物件來管理記憶體的分配,簡化了 new 和 delete 的呼叫,我們也可以從這個輕量級的記憶體池物件學習 google 大神工程師是如何管理記憶體的。arena 記憶體管理模型 arena 使用下面幾個成員變...
leveldb原始碼分析 之 入門使用
leveldb是google開源的乙個key value儲存引擎庫,類似於開源的lucene索引庫一樣。其他的軟體開發者可以利用該庫做二次開發,來滿足定製需求。leveldb採用日誌式的寫方式來提高寫效能,但是犧牲了部分讀效能。為了彌補犧牲了的讀效能,一些人提議使用ssd作為儲存介質。對於本地化的k...
leveldb原始碼分析1
leveldb是乙個key value型的儲存引擎,由google開發,並宣布在bsd許可下開放源 plain git clone plain cd leveldb make all 此時生成libleveldb.a庫檔案。拷貝leveldb的標頭檔案到 usr include下 plain cp ...