亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

LevelDB的功能特性是什么

發布時間:2022-01-17 09:52:17 來源:億速云 閱讀:141 作者:iii 欄目:數據庫

本文小編為大家詳細介紹“LevelDB的功能特性是什么”,內容詳細,步驟清晰,細節處理妥當,希望這篇“LevelDB的功能特性是什么”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。

打開和關閉

LevelDB 的數據存儲在一個特定的目錄中,里面有很多數據文件、日志文件等。使用 LevelDB API 來打開這個目錄,就得到了 db 的引用。后續我們就使用這個 db 引用來執行讀寫操作。下面的代碼是 Java 語言描述的偽代碼。

class LevelDB {
  public static LevelDB open(String dbDir, Options options);
  void close(); // 關閉數據庫
}


打開數據庫有很多選項可以配置,比如設置塊緩存大小、壓縮等

基礎 API

LevelDB 用起來就像 HashMap,但是比 HashMap 要稍微弱一些,因為 put 方法不能返回舊值,delete 操作也不知道對應的 key 是否真的存在。

class LevelDB {
  byte[] get(byte[] key)
  void put(byte[] key, byte[] value)
  void delete(byte[] key)
  ...
}

原子批處理

對于多個連續的寫操作如果因為宕機有可能導致這多個連續的寫操作只完成了一部分。為此 LevelDB 提供了批處理功能,批處理操作就好比事務,LevelDB 確保這一些列寫操作的原子性執行,要么全部生效要么完全不生效。

class WriteBatch {
  void put(byte[] key, byte[] value);
  void delete(byte[] key);
}

class LevelDB {
  ...
  void write(WriteBatch wb);
}

日志文件

當我們調用 LevelDB 的 put 方法往庫里寫數據時,它會先將數據記錄到內存中,延后再通過某種特殊的策略持久化到磁盤。這就存在一個問題,如果突發宕機,這些來不及寫到磁盤的數據就丟失了。所以 LevelDB 也采用了和 Redis AOF 日志類似的策略,先講修改操作的日志寫到磁盤文件中,再進行實際的寫操作流程處理。

如此即使宕機發生了,數據庫啟動時還可以通過日志文件來恢復。

安全寫(同步寫)

了解 Redis 的同學都知道它的 AOF 寫策略有多種配置,取決于日志文件同步磁盤的頻率。頻率越高,遇到宕機時丟失的數據就越少。操作系統要將內核中文件的臟數據同步到磁盤需要進行磁盤 IO,這會影響訪問性能,所以通常都不會同步的太頻繁。

LevelDB 也是類似的,如果使用前面的非安全寫,雖然 API 調用成功了,但是遇到宕機問題,有可能對應的操作日志會丟失。所以它提供了安全寫操作,代價就是性能會變差。

class LevelDB {
  ...
  void putSync(byte[] key, byte[] value);
  void deleteSync(byte[] key);
  void writeSync(WriteBatch wb);
}


在安全和性能之間往往需要折中,所以通常我們會定時若干毫秒或者每隔若干寫操作使用一次同步寫。這樣可以在兼顧寫性能的同時盡量少丟失數據。

并發

LevelDB 的磁盤文件會放在一個文件目錄中,里面有很多相關的數據和日志文件。它不支持多進程同時打開這個目錄來使用 LevelDB API 進行讀寫訪問。但是對于同一個進程 LevelDB API 是支持多線程安全讀寫的。LevelDB 內部會使用特殊的鎖來控制并發操作。

遍歷

LevelDB 中的 Key 都是有序的,按照字典序從小到大整齊排列。LevelDB 提供了遍歷 API 可以逐個順序訪問所有的鍵值對,可以指定從中間開始遍歷。

class LevelDB {
  ...
  Iterator<KV> scan(byte[] startKey, byte[] endKey, int limit);
}

快照隔離

LevelDB 支持多線程并發讀寫,這意味著連續的兩個同樣 key 的讀操作讀到的數據可能不一樣,因為兩個讀操作中間數據可能被其它線程修改了。這在數據庫理論中稱為「重復讀」。LevelDB 提供了快照隔離機制,在同一個快照范圍內保證連續的讀寫操作不受其它線程修改操作的影響。

class Snapshot {
  byte[] get(byte[] key)
  void put(byte[] key, byte[] value)
  void delete(byte[] key)
  void write(WriteBatch wb);
  ...
  void close();  // 關閉快照
}

class LevelDB {
  ...
  Snapshot getSnapshot();
}


快照雖然很神奇,但是實際上它的原理非常簡單,這個我們后文再深入講解。

自定義 Key 比較器

LevelDB 的 key 默認使用字典序,不過它也提供了自定義排序規則。你可以自定義一個排序函數注冊進去,比如按數字排序。必須盡可能確保排序規則在整個數據庫生命周期內保持不變,因為排序會影響到磁盤鍵值對的存儲順序,磁盤存儲順序是無法動態改變的。

Options options = new Options();
options.comparator = new CustomComparator();
db = LevelDB.open("/tmp/ldb", options);


自定義比較器很危險,謹慎使用。比較算法設置不當,會嚴重影響到存儲效率。如果確實必須要改變排序規則,那就需要提前規劃,這里會有一個特別的小技巧,理解它需要了解磁盤存儲的細節,所以我們后續再仔細探討。

數據塊

LevelDB 的磁盤數據是以數據庫塊的形式存儲的,默認的塊大小是 4k。適當提升塊大小將有益于批量大規模遍歷操作的效率,如果隨機讀比較頻繁,這時候塊小點性能又會稍好,這就要求我們自己去折中選擇。

Options options = new Options();
options.blockSize = 8092;
db = LevelDB.open("/tmp/ldb", options);


塊不宜過小低于 1k,也不宜過大設置成了好幾 M,這樣過激的設置并不會給性能帶來多大的提升,反而會大幅增加數據庫在不同的讀寫場合的性能波動。我們要選擇中庸之道,在默認塊大小周邊浮動。塊大小一經初始化就不可再次更改。

壓縮

LevelDB 的磁盤存儲默認是開啟壓縮的,是業界常用的 Snappy 算法,壓縮效率非常高,所以無需擔心性能損耗問題。如果你不想使用壓縮,也可以動態關閉。關閉壓縮開關通常不會帶來明顯的性能提升,所以我們盡可能不要去動它。

Options options = new Options();
options.compression = CompressionType.kSnappyCompression;
// options.compression = CompressionType.kNoCompression; // 關閉壓縮
db = LevelDB.open("/tmp/ldb", options);

塊緩存

LevelDB 的內存中存儲了一筆最近讀寫的熱數據,如果請求的數據在熱數據中查不到就需要去磁盤文件中去查找,效率就會大幅降低。LevelDB 為了降低磁盤文件的搜尋次數,增加了塊緩存,緩存了近期頻繁使用的數據塊解壓縮之后的內容。

Options options = new Options();
options.blockCache = LevelDB.NewLRUCache(100 * 1024 * 1024); // 100M
db = LevelDB.open("/tmp/ldb", options);


默認塊緩存不開啟,打開數據庫時可以手動設置選項。塊緩存會占據一部分內存,不過這通常不需要設置太大,100M 左右就差不多了,再大一些效率提升的也不明顯了。

還需要注意遍歷操作對緩存的影響,為了避免遍歷操作將很多冷門數據刷到塊緩存中,可以在遍歷的時候設置一個選項 fill_cache,它用來控制磁盤遍歷的數據塊是否需要同步到緩存。

布隆過濾器

內存讀 miss 導致磁盤搜尋是一個比較耗時的操作,LevelDB 為了進一步減少磁盤讀的次數,在每個磁盤文件上又加了一層布隆過濾器,它需要消耗一定的磁盤空間,但是在效果上可以直接將磁盤讀次數大幅減少。布隆過濾器的數據存儲在磁盤文件中數據塊的后面。

LevelDB 的磁盤文件是分層存儲的,它會先去 Level 0 查找,如果找不到繼續去 Level 1 去找,一直遞歸到最底層。所以如果你去找一個不存在的 key,就需要很多次磁盤文件讀操作,會非常耗費時間。而布隆過濾器可以幫你省去95%以上的磁盤文件搜尋的時間。

布隆過濾器類似于一個內存 Set 結構,它里面存儲了指定磁盤文件一定范圍內所有 Key 的指紋信息。當它發現某個 key 的指紋在 Set 集合里找不到,它就可以斷定這個 key 肯定不存在。

如果對應的指紋可以在集合里找到,這并不能確定它就一定存在。因為不同的 Key 可能會生成同樣的指紋,這就是布隆過濾器的誤判率。誤判率越低需要的 Key 指紋信息越多,對應消耗的內存空間也就越大。

如果布隆過濾器能準確知道某個 Key 是否存在,那就不存在誤判了,這時候也就不會存在白白浪費的磁盤讀操作。這樣的極限形式的布隆過濾器就是 HashSet —— 內存里存儲了所有的 Key,當然內存空間自然是無法接受的。

Options options = new Options();
// 每個 key 的指紋大小是 10bit
options.filterPolicy = LevelDB.NewBloomFilterPolicy(10);
db = LevelDB.open("/tmp/ldb", options);


在使用布隆過濾器時,我們需要在內存消耗和性能之間做一個折中選擇。如果你想深入理解布隆過濾器的原理,可以去看《Redis 深度歷險》,里面有一個單獨的章節專門講解布隆過濾器的內部原理。

默認布隆過濾器沒有打開,需要在打開數據庫的時候設置 filter_policy 參數才可以生效。布隆過濾器是減少磁盤讀操作的最后一層堡壘。布隆過濾器內部的位圖數據會存儲在磁盤文件中,但是使用是會緩存在內存里面。

數據校驗

LevelDB 有嚴格的數據校驗機制,它將校驗的單位精確到了 4K 字節的數據塊。校驗和會浪費一點存儲空間和計算時間,但是在遇到數據塊損壞時可以較為精確地恢復健康的數據。

class LevelDB {
  ...
  public void static repairDB(String dbDir, Options options);
}


打開數據庫時默認沒有開啟強制校驗選項,如果開啟了,在遇到校驗錯誤時就會報錯。如果數據真的出現了問題,LevelDB 還提供了修復數據的方法 repairDB() 可以幫我們恢復盡可能多的數據。

讀到這里,這篇“LevelDB的功能特性是什么”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

环江| 宜城市| 米脂县| 枞阳县| 金溪县| 花莲县| 德钦县| 台州市| 安宁市| 长丰县| 阿瓦提县| 安乡县| 海兴县| 南岸区| 于田县| 乌拉特后旗| 汾阳市| 安陆市| 龙川县| 海口市| 泸水县| 札达县| 昆明市| 大渡口区| 博白县| 安龙县| 贵港市| 菏泽市| 丰县| 疏附县| 措美县| 察雅县| 响水县| 潼关县| 长岛县| 隆化县| 乐业县| 寻甸| 平乐县| 都安| 扎兰屯市|