leveldb原理和使用
LevelDB是一個基于本地文件的存儲引擎,非分布式存儲引擎,原理基于BigTable(LSM文件樹),無索引機制,存儲條目為Key-value。適用于保存數據緩存、日志存儲、高速緩存等應用,主要是避免RPC請求帶來的延遲問題。在存取模型上,順序讀取性能極高,但是對于隨機讀取的情況延遲較大(但性能也不是特別低),比較適合順序寫入(key),隨機的key寫入也不會帶來問題。數據存量通常為物理內存的3~5倍,不建議存儲過大的數據,在這個數據量級上,leveldb的性能比那些“分布式存儲”要高(即本地磁盤存取延遲小于RPC網絡延遲)。
? ? 1)如果你的log日志或者視頻片段需要暫存在本地,稍后再批量發給遠端的數據中心,那么這種需求非常適合使用leveldb做數據緩沖。(這些緩存的數據被切分成多個小的chunks,以key-value的方式保存在leveldb中)
? ? 2)如果你希望構建一個本地cache組件,但是cache的數據可能比內存容量要大,此時我們就可以使用leveldb做支撐,leveldb將一部分熱區數據保存在內存,其他數據保存在磁盤上,可以并發的、隨機讀取key-value。但是數據不能太大,否則磁盤讀取的延遲將很大,此時應該使用分布式緩存。(當然,分布式緩存是用于解決分布式環境中數據同步、一致性的問題,不僅僅是數據量過大的問題)
?
一、原理
1、Files
? ? leveldb的實現類似于Bigtable中的一個tablet(Google),只不過底層的文件組織形式稍有不同。
? ? 每個Database有一系列本地文件組成,這些文件有不同的類型:
? ??Log文件
? ? log文件存儲了一序列的最近更新操作,每個更新(update)都會append到當前log文件的尾部,當log文件的尺寸達到預設定的大小時,將會把此log文件轉換成一個sorted table(.sst)文件,然后滾動創建一個新的log文件來保存此后的updates操作,主要是用于數據恢復。
?
? ? 當前log文件的數據copy被保存在一個內存結構中,稱為memtable,任何update首先會被寫入到memtable中,然后在寫入log文件。每個read操作都會首先訪問memtable,如果memtable中沒有的話再觸發磁盤檢索(如果開啟了cacheSize,則在磁盤檢索之前會查看cache),因此這些update數據都可以在read操作中反應出來。
?
? ? memtable中的數據按照key順序存儲,即有序存儲(基于跳躍表實現)。默認memtable的大小為4M,由參數“writeBufferSize”決定,需要在leveldb打開db文件時指定。
?
? ??Sorted tables(簡稱SST)
? ? 當memtable的數據量達到閥值時,則會被刷新寫入到磁盤,生成一個Sorted table(.sst)文件,此文件存儲了一序列按照key排序的entries,每個entry可以是key-value,或者是一個key的刪除標記(marker),文件和memtable一樣也是根據key排序的。(刪除標記可以屏蔽掉先前sst文件中保存的較舊的數據,即如果一個key被標記為刪除,那么先前的sst文件中關于此key的數據,將不會被read到)
?
? ? Sorted tables按照層次(level)進行組織,由log文件生成的SST將會放置在一個特殊的young level中--即level-0,當young level中SST文件的個數超過一個閥值(4個),這些young文件將會與level-1中的那些有數據重疊的文件合并,并生成一序列新的level-1文件(每個新文件大小位2M)。
?
? ? 備注:“重疊”意義為key區間在兩個文件中都存在。keys在SST文件中保存是嚴格排序的。同時需要注意,sst文件中還包含BloomFilter內容,bloomFilter可以快速判斷key是否存在于此sst文件,有效的提高了read的效率。
?
? ??young level中的文件可能包含重疊的keys,不過其他level中的SST文件只會包含不同的“非重疊”的keys區間。假如level-L,其中L >= 1,當level-L中SST文件的總大小達到(10^L)MB時(例如level-1位10MB,level-2位100MB),那么level-L中的一個文件,將會和level-(L+1)中那些有keys重疊(覆蓋)的文件merged,并生成一組新的level-(L+1)文件。這些merge,只通過批量的文件讀寫操作,即可將最新的updates數據從young level遷移到最高的level。同一個level中,不會有key重疊的sst文件;但是不同level可能會有!
? ?level的級別越低,數據新鮮程度越高。遍歷數據時,從level 0開始向高level推進。
?
? ??Manifest(清單)
? ??manifest文件中列舉了構成每個level的SST文件列表,以及相應的key區間,還包括一些重要的metadata。當database被reopened時,都會創建一個新的manifest文件(文件命中包含一個新的number序列號)。manifest文件的格式像log,“serving data”的變更(比如SST文件的創建、刪除)操作都會被append到此log中。
?
? ??Current
? ? CURRENT文件是一個簡單的文本文件,保存了當前最新的manifest文件的名稱。
?
? ? 其他:略
?
2、Level 0
? ? 當log文件的尺寸增長到一定的大小(默認1M):
- 創建一個新的memtable和log文件,用來保存此后的updates操作。
- 在后臺:將舊的memtable寫入到文件生成新的SST文件,然后銷毀此memtable。刪除舊的log文件,然后將此新的SST文件添加到young level組織中。
3、Compactions
? ??當level-L的尺寸達到了它的限制,我們將使用一個后臺線程對它進行Compaction。壓縮時,將會從level-L中選擇一個文件,同時選擇level-(L+1)中所有與此文件key有重疊的文件。如果level-L中一個文件只與level-(L+1)中某個文件的一部分重疊,那么level-(L+1)中的此文件作為壓縮時的輸入,在壓縮結束后,此文件將被拋棄。不過,level-0比較特殊(文件中的keys可能互相重疊),對于level-0到level-1的壓縮我們需要特殊處理:level-0中文件中互相重疊的話,那么將可能一次選擇多個level-0的文件作為輸入。
?
? ??壓縮將選擇的文件內容重新輸出到一序列新的level-(L+1)文件中(多路合并),當每個輸出文件達到2M時將會切換一個新的文件,或者當新輸出的文件中key區間覆蓋了level-(L+2)中多于10個文件時,也會切換生成新文件;第二個規則保證此后level-(L+1)的壓縮時無需選擇太多的文件。
?
? ? 當level-(L+1)中的新文件加入到“serving state”時,那么舊的文件將會被刪除(包括level-L和level-(L+1))。
?
? ??壓縮時,將會拋棄那些“overwritten”的值;如果遇到刪除標記,且對應的key在更高的level中不存在,也會直接拋棄。
?
? ??Timing
? ? level-0將會讀取4個1M的文件(每個1M,level-0最多4個文件),最壞的情況是讀取level-1的所有文件(10M),即我們讀寫各10MB。
? ? 和level-0不同,對于其他level-L,我們將讀取2M的一個文件,最壞的情況是它與level-(L+1)中12文件有重疊(10個文件,同時還有2個處于邊界的文件);那么一次壓縮將讀寫26MB數據。假定磁盤IO速率位100M/S,那么一次壓縮耗時大約0.5秒。
?
? ? 如果我們對磁盤速率受限,比如10M/S,那么壓縮可能耗時達到5秒。
?
? ??文件個數
? ??每個SST文件的大小為2M(level-0的除外),事實上我們可以通過增大此值(需要重新編譯源文件),來減少文件的總數,不過這會導致壓縮更加耗時(讀取的文件尺寸更大,磁盤密集操作);另外,我們可以將不同的文件放在多個目錄中。
?
4、數據恢復
? ? 1)從CURRENT中讀取最新的manifest文件的名字。
? ? 2)讀取manifest文件。
? ? 3)清理那么過期的文件。
? ? 4)我們可以打開所有的SST文件,不過通常lazy更好。
? ? 5)將log存留文件轉存成新的level-0中的SST文件。
? ? 6)引導write操作到新的log文件中。
? ? 7)回收垃圾文件。
?
? ? 每次壓縮和recovery操作后,將會調用DeleteObsoleteFiles():從database中查詢出所有的file的名字,然后將當前log文件之外的其他log文件全出刪除,刪除那些所有level中都不包含的、以及壓縮操作沒有引用的SST文件。
?
二、使用
? ? leveldb為一個本地化的K-V存儲數據庫,設計思想類似于Bigtable,將key按照順序在底層文件中存儲,同時為了加快讀取操作,內存中有一個memtable來緩存數據。
? ? 根據leveldb官網的性能基準測試,我們大概得出其特性:
? ? 1)leveldb的順序讀(遍歷)的效率極高,幾乎接近文件系統的文件順序讀。比BTree數據庫要快多倍。
? ? 2)其隨機讀性能較高,但和順序讀仍有幾個量級上的差距。leveldb的隨機讀,和基于BTree的數據庫仍有較大差距。(個人親測,其隨機讀的效率并不像官網所說的如此之高,可能與cache的配置有關)隨機讀,要比BTree慢上一倍左右。
? ? 3)順序寫,性能極高(無強制sync),受限于磁盤速率;隨機寫,性能稍差,不過性能相對于其他DB而言,仍有極大的優勢。無論是順序寫還是隨機寫,性能都比BTree要快多倍。
? ? 4)leveldb為K-V存儲結構,字節存儲。屬于NoSql數據庫的一種,不支持事務,只能通過KEY查詢數據;支持批量讀寫操作。
? ? 5)leveldb中key和value數據尺寸不能太大,在KB級別,如果存儲較大的key或者value,將對leveld的讀寫性能都有較大的影響。
? ? 6)leveldb本身沒有提供索引機制,所以隨機讀性能稍差。它存儲的key、value可以為任意字節數組。
?
? ? 因為leveldb本身尚不具備“分布式”集群架構能力,所以,我們將有限的數據基于leveldb存儲(受限于本地磁盤)。
? ??
? ??案例推演:
? ? 1)leveldb具備“cache + 磁盤持久存儲”特性,且不支持RPC調用,那么leveldb需要和application部署在同一宿主機器上。類似于“嵌入式”K-V存儲系統。
? ? 2)如果存儲數據較少,3~5G,且“讀寫比”(R:W)較高,我們可以讓leveldb作為本地cache來使用,比如Guava cache + leveldb,這種結合,可以實現類似于輕量級redis。即作為本地緩存使用。通常LevelDB存儲的數據是內存大小的3~5倍(現代的操作系統配置),不建議用leveldb存儲過大的數據,否則性能將下降很大。
? ? 3)如果數據較多,通常為“順序讀”或者“順序寫”,我們可以將leveldb作為Hadoop HDFS的“微縮版”,可以用來緩存高峰期的消息、日志存儲的緩沖區。比如我們將用戶操作日志暫且存儲在leveldb中,而不是直接將日志發送給remote端的Hadoop(因為每次都直接調用RPC,將會對系統的吞吐能力帶來極大的影響),而是將這些頻繁寫入的日志數據存儲在本地的leveldb中,然后使用后臺線程以“均衡”的速度發送出去。起到了“Flow Control”(流量控制)的作用。
?
? ? 其中ActiveMQ即采用leveldb作為底層的消息數據存儲,性能和容錯能力很強。在很多情況下,leveldb可以作為本地log、IO緩沖文件的存儲方案。
?
三、API簡析(JAVA版)
? ? 原生leveldb是基于C++開發,java語言無法直接使用;iq80對leveldb使用JAVA語言進行了“逐句”重開發,經過很多大型項目的驗證(比如ActiveMQ),iq80開發的JAVA版leveldb在性能上損失極少(10%)。對于JAVA開發人員來說,我們直接使用即可,無需額外的安裝其他lib。
? ??1、pom.xml
Java代碼???
? ? 2、代碼樣例
Java代碼????
? ? LevelDB是google的實現,官方只提供了C++版的客戶端,java客戶端比如上述的iq80(還有fusesource 項目的leveldbjni)是來自社區的。不過BigTable的設計思想和LevelDB的特性被社區延續了下去,比如相對比較完善和性能更加優秀的RocksDB,我們建議在實際的開發工作中采用它。
?
參考文獻:
1、LevelDB實現原理:http://leveldb.googlecode.com/git-history/1.17/doc/impl.html
2、LevelDB性能測試:http://leveldb.googlecode.com/git-history/1.17/doc/benchmark.html?r=1.17
轉載于:https://www.cnblogs.com/mtcnn/p/9410037.html
總結
以上是生活随笔為你收集整理的leveldb原理和使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS使用学习总结
- 下一篇: Windows开启WMI时一些总结