日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

leveldb原理和使用

發布時間:2025/7/25 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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代碼??
  • <dependency>??
  • ????<groupId>org.iq80.leveldb</groupId>??
  • ????<artifactId>leveldb</artifactId>??
  • ????<version>0.7</version>??
  • </dependency>??
  • <dependency>??
  • ????<groupId>org.iq80.leveldb</groupId>??
  • ????<artifactId>leveldb-api</artifactId>??
  • ????<version>0.7</version>??
  • </dependency>??
  • ?

    ? ? 2、代碼樣例

    Java代碼??
  • boolean?cleanup?=?true;??
  • Charset?charset?=?Charset.forName("utf-8");??
  • String?path?=?"/data/leveldb";??
  • ??
  • //init??
  • DBFactory?factory?=?Iq80DBFactory.factory;??
  • File?dir?=?new?File(path);??
  • //如果數據不需要reload,則每次重啟,嘗試清理磁盤中path下的舊數據。??
  • if(cleanup)?{??
  • ????factory.destroy(dir,null);//清除文件夾內的所有文件。??
  • }??
  • Options?options?=?new?Options().createIfMissing(true);??
  • //重新open新的db??
  • DB?db?=?factory.open(dir,options);??
  • ??
  • //write??
  • db.put("key-01".getBytes(charset),"value-01".getBytes(charset));??
  • ??
  • //write后立即進行磁盤同步寫??
  • WriteOptions?writeOptions?=?new?WriteOptions().sync(true);//線程安全??
  • db.put("key-02".getBytes(charset),"value-02".getBytes(charset),writeOptions);??
  • ??
  • ??
  • //batch?write;??
  • WriteBatch?writeBatch?=?db.createWriteBatch();??
  • writeBatch.put("key-03".getBytes(charset),"value-03".getBytes(charset));??
  • writeBatch.put("key-04".getBytes(charset),"value-04".getBytes(charset));??
  • writeBatch.delete("key-01".getBytes(charset));??
  • db.write(writeBatch);??
  • writeBatch.close();??
  • ??
  • //read??
  • byte[]?bv?=?db.get("key-02".getBytes(charset));??
  • if(bv?!=?null?&&?bv.length?>?0)?{??
  • ????String?value?=?new?String(bv,charset);??
  • ????System.out.println(value);??
  • }??
  • ??
  • //iterator,遍歷,順序讀??
  • ??
  • //讀取當前snapshot,快照,讀取期間數據的變更,不會反應出來??
  • Snapshot?snapshot?=?db.getSnapshot();??
  • //讀選項??
  • ReadOptions?readOptions?=?new?ReadOptions();??
  • readOptions.fillCache(false);//遍歷中swap出來的數據,不應該保存在memtable中。??
  • readOptions.snapshot(snapshot);//默認snapshot為當前。??
  • DBIterator?iterator?=?db.iterator(readOptions);??
  • while?(iterator.hasNext())?{??
  • ????Map.Entry<byte[],byte[]>?item?=?iterator.next();??
  • ????String?key?=?new?String(item.getKey(),charset);??
  • ????String?value?=?new?String(item.getValue(),charset);//null,check.??
  • ????System.out.println(key?+?":"?+?value);??
  • }??
  • iterator.close();//must?be??
  • ??
  • //delete??
  • db.delete("key-01".getBytes(charset));??
  • ??
  • //compaction,手動??
  • ??
  • db.compactRange("key-".getBytes(charset),null);??
  • ??
  • //??
  • db.close();??
  • ??

    ? ? 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原理和使用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。