RocksDB介紹:一個比LevelDB更彪悍的引擎
RocksDB基本介紹:
嵌入式數據庫RocksDB是非死book基于LevelDB開發的一種嵌入式Key-value存儲系統,該數據庫能夠充分利用閃存的性能,大大提升應用服務器的速度。
Rocksdb. 這個開源引擎是基于 Google 的 leveldb 1.5 版本, 但據稱做了許多優化, 性能相對 leveldb 有了很大的提升, 而且解決了 leveldb 主動限制寫的問題.
非死book 用RocksDB來驅動一些面向用戶的應用,這些應用由于需要通過網絡訪問外部存儲而性能低下,此外非死book還用RocksDB來解決固態硬盤 IO利用率不高相關的一些問題。非死book的數據庫工程師Dhruba Borthakur在其個人博客介紹了RocksDB的設計原由和原理,但實際上催生RocksDB的最大動力來自服務器閃存存儲卡的價格大幅下滑,非死book的定制服務器已經開始全面采用閃存。
隨著閃存存儲時代的到來,一些新的應用可以在閃存中管理并快速訪問自己的數據集,無需通過網絡訪問外部數據。這些新應用使用的就是我們所說的嵌入式數據庫。
數據庫查詢如果在本地閃存中進行,速度理論上會比通過數據中心內部網絡查詢快一倍,因為數據庫中心內部網絡有50微妙的延遲。
RocksDB 的能夠充分利用閃存的高IOPS性能,同時也能利用多核服務器的計算性能,非死book目前已經在RocksDB的GitHub頁面上發布了 RocksDB在Fusion-io服務器上的跑分基準測試結果,非死book聲稱其速度比Google的LevelDB嵌入式key-value存儲系統快很多。
RocksDB和LevelDB的區別:
關于LevelDB的資料網上還是比較豐富的,如果你尚未聽說過LevelDB,那請稍微預習一下,因為RocksDB實際上是在LevelDB之上做的改進。本文主要側重在架構上對RocksDB對LevelDB改進的地方做個簡單介紹并添加一些個人的看法,更詳細的信息讀者可參考其官網:http://rocksdb.org/
RocksDB是在LevelDB原來的代碼上進行改進完善的,所以在用法上與LevelDB非常的相似。如下,就是簡單的把原來Leveldb信息替換為Rocksdb,從繼承的角度看,Rocksdb就像是Leveldb的后輩。
RocksDB:
#include "rocksdb/db.h" rocksdb::DB* db; rocksdb::Options options; options.create_if_missing = true; rocksdb::Status status = rocksdb::DB::Open(options, "/tmp/testdb", &db); assert(status.ok()); status = db->Get(rocksdb::ReadOptions(), key1, &value); status = db->Put(rocksdb::WriteOptions(), key2, value); status = db->Delete(rocksdb::WriteOptions(), key1); delete db;
LevelDB:
#include "leveldb/db.h" leveldb::DB *db; leveldb::Options options; options.create_if_missing = true; leveldb::Status status = leveldb::DB::Open(options, "/tmp/testdb", &db); assert(status.ok()); status = db->Get(leveldb::ReadOptions(), key1, &value); status = db->Put(leveldb::WriteOptions(), key2, value); status = db->Delete(leveldb::WriteOptions(), key1); delete db;
RocksDB 雖然在代碼層面上是在LevelDB原有的代碼上進行開發的,但卻借鑒了Apache HBase的一些好的idea。在云計算橫行的年代,開口不離Hadoop,RocksDB也開始支持HDFS,允許從HDFS讀取數據。而 LevelDB則是一個比較單一的存儲引擎,有點我就是我,除了我依然只有我的感覺。也是因為LevelDB的單一性,在做具體的應用的時候一般需要對其作進一步擴展。
RocksDB支持一次獲取多個K-V,還支持Key范圍查找。LevelDB只能獲取單個Key
RocksDB 除了簡單的Put、Delete操作,還提供了一個Merge操作,說是為了對多個Put操作進行合并。站在引擎實現者的角度來看,相比其帶來的價值,其實現的成本要昂貴很多。個人覺得有時過于追求完美不見得是好事,據筆者所測(包括測試自己編寫的引擎),性能的瓶頸其實主要在合并上,多一次少一次Put 對性能的影響并無大礙。
RocksDB提供一些方便的工具,這些工具包含解析sst文件中的K-V記錄、解析MANIFEST文件的內容等。有了這些工具,就不用再像使用LevelDB那樣,只能在程序中才能知道sst文件K-V的具體信息了。
RocksDB 支持多線程合并,而LevelDB是單線程合并的。LSM型的數據結構,最大的性能問題就出現在其合并的時間損耗上,在多CPU的環境下,多線程合并那是 LevelDB所無法比擬的。不過據其官網上的介紹,似乎多線程合并還只是針對那些與下一層沒有Key重疊的文件,只是簡單的rename而已,至于在真正數據上的合并方面是否也有用到多線程,就只能看代碼了。
RocksDB增加了合并時過濾器,對一些不再符合條件的K-V進行丟棄,如根據K-V的有效期進行過濾。
壓縮方面RocksDB可采用多種壓縮算法,除了LevelDB用的snappy,還有zlib、bzip2。LevelDB里面按數據的壓縮率(壓縮后低于75%)判斷是否對數據進行壓縮存儲,而RocksDB典型的做法是Level 0-2不壓縮,最后一層使用zlib,而其它各層采用snappy。
在故障方面,RocksDB支持增量備份和全量備份,允許將已刪除的數據備份到指定的目錄,供后續恢復。
RocksDB支持在單個進程中啟用多個實例,而LevelDB只允許單個實例。
RocksDB 支持管道式的Memtable,也就說允許根據需要開辟多個Memtable,以解決Put與Compact速度差異的性能瓶頸問題。在LevelDB里面因為只有一個Memtable,如果Memtable滿了卻還來不及持久化,這個時候LevelDB將會減緩Put操作,導致整體性能下降。筆者目前寫的引擎在這方面竟然跟RocksDB不謀而合,這里偷偷樂一下,呵呵。
看完上面這些介紹,相比LevelDB是不是覺得RocksDB彪悍的不可思議,很多該有的地方都有,該想的都想到了,簡直不像在做引擎庫,更像是在做產品。不過雖然RocksDB在性能上提升了不少,但在文件存儲格式上跟LevelDB還是沒什么變化的, 稍微有點更新的只是RocksDB對原來LevelDB中sst文件預留下來的MetaBlock進行了具體利用。
個人覺得RocksDB尚未解決的地方:
- 依然是完全依賴于MANIFEST,一當該文件丟失,則整個數據庫基本廢掉。
- 合并上依然是整個文件載入,一些沒用的Value將被多次的讀入內存,如果這些Value很大的話,那沒必要的內存占用將是一個可觀的成本。
關于這兩個問題,尤其是后面那個問題,筆者已有相應的解決方案,至于結果如何只等日后實現之后再作解說了。
如何給RocksDB使用類似C/S的架構:
為了滿足各位對 非死book 出品的 rocksdb 的愛好, SSDB 數據庫也可以使用 rocksdb.
這個項目就是 ssdb-rocks: https://github.com/ideawu/ssdb-rocks
據說 rocksdb 性能不錯, 在某些場景比 leveldb 更佳, 歡迎各位試用. 注意, rocksdb 和 leveldb 不兼容, 所以, 舊數據不能直接用于這兩個引擎. 原來你用了 leveldb, 就能不直接換成 rocksdb, 你必須自己寫腳本導數據.
RocksDB官網:http://www.rocksdb.org/
RocksDB源碼:https://github.com/非死book/rocksdb/