RocksDB原理及應用


RocksDB是啥

LSM 類存儲引擎、數據庫之一。所謂LSM,一般的名字叫 Log Structured-Merge Tree(日志結構合並樹),來源於分布式數據庫領域,也是BigTable 的論文中所使用的文件組織方式。它的特點在於寫入的時候是append only的形式,就像名字所顯示的那樣,跟日志一樣只在文件后面追加。

LSM 樹結構的問題: 寫入速度快,讀取速度慢,寫放大和讀放大都較高。

Rocksdb本身支持單個kv的讀寫和批量kv值的讀寫。由於LSM的出身,它專注於利用LSM樹的特性,適應有序、層次化的磁盤讀寫。在LSM樹之上構建了Rocksdb,而在RocksDB之上同樣有一些更面向應用層的數據庫,包括分布式數據庫、查詢引擎、大數據存儲引擎、圖數據庫如Janusgraph等。

一、RocksDB 的讀寫層次和數據結構

rocksdb的數據寫入結構如下:

在內存有memtable,

磁盤有WAL文件目錄和SST文件目錄。

 

memtable和SST文件

內存中的數據和SST文件組成了RocksDB數據的全集。

rocksdb中的數據結構有三種,分別是skiplist、hash-skiplist、hash-linklist;跟leveldb不同。跳表的好處在於插入的時候可以保證數據的有序,支持二分查找、范圍查詢。當然,刪除的時候不是立即刪除,因為會影響到數據的寫放大,一般是在compact階段進行真正的刪除。

hash-skiplist的索引既有hash索引,又有skiplit的二分索引,針對於有明確key或教完整key前綴的查詢,如果要進行全表掃描,會非常耗費內存及低性能,因為要產生臨時的有序表; hash-linklist也是一樣的道理。

         對於hash-skiplist, 讀取時候的緩存分片如下:

 

 

SST文件結構類型

SSTable是一種數據結構,當memtable達到一定上限之后,會flush到disk形成immutable Sorted String Table (SSTable);Rocksdb寫入數據,即put的時候,在磁盤中保存level層次樹,在單個level中超出大小的時候進行合並;合並的時候是支持多線程並發的;

 

 

 

 

緊湊compact操作

compact,緊湊,可譯作壓縮。

為了提高讀性能,一般會做compact操作。compact操作將數據根據key進行合並,刪除了無效數據,縮短了讀取路徑,優化了范圍查詢。

把內存里面不可變的Memtable給dump到到硬盤上的SSTable層中,叫做minor compaction。

SStable里的合並稱為major compaction。由於major compaction耗費CPU和磁盤性能,HBase中需要在高峰時段禁用major compaction。

 

寫放大:一份數據被順序寫幾次(還是那一份數據,只是需要一直往下流)。第一次寫到L0,之后由於compaction可能在L1~LN各寫一次

讀放大:一個query會在多個sstable,而這些sstable分布在L0~LN

空間放大:一份原始數據被copy/overwritten(一份用於服務query,一份用於snapshot之后用於compaction)

下面是一些典型的Compaction技巧,可以看到,沒有寫放大,讀放大,空間放大三者都很完美的方案:

  Leveled Compaction

  • 全部層級都按照標准的從上到下進行層級合並
  • 讀寫放大都比較嚴重,但是空間放大較低
  • 在這篇論文(Towards Accurate and Fast Evaluation of Multi-Stage Log-Structured Designs)中有詳細的闡述

  Tiered Compaction

  • 即 RocksDB 中的 Universal Compaction
  • 空間放大和讀放大嚴重,但是寫放大最小
  • 在這篇論文(Dostoevsky: Better Space-Time Trade-Offs for LSM-Tree Based Key-Value Stores via Adaptive Removal of Superfluous Merging)有詳細的闡述

  Tiered+Leveled Compaction

  • 即 RocksDB 中的 Level Compaction
  • 是一個混合的 Compaction 策略,空間放大比 Tiered Compaction 更低,寫放大也比 Leveled 低

  Leveled-N Compaction

  • 比 Leveled Compaction 寫放大更低,讀放大更高
  • 一次合並 N - 1 層到第 N 層

二、分布式Rocksdb的改造

Read-Only模式下可以讀取數據,但是數據並不一定是最新的;Read Secondary Instance模式下可以讀到最新的數據,實現方式是redo WAL(重放日志);

可用性

多節點下可用性比較重要;一般是通過WAL實現重放,但是WAL仍然會遇到磁盤損壞的問題。常用的,拍腦門能想出來的解決方式是增加數據一致性的模塊;

數據一致性

分布式數據庫的一致性協議需要共識協議作為基礎,rocksdb之上加入一層raft協議可以實現分布式一致性,實現該協議也是為了盡可能地提高可用性,盡管在系統陷入到節點數超過一半掛掉的情況是無法保證完好的一致性的。

但是這樣子會加深寫放大的問題。對此可以使用寫放大較低的compact策略緩解;對於系統層面,如果不想寫放大太多,也可以減少raft寫及數據備份,直接將讀節點的比例增加。(都不是完美的方案)

 

事務處理

在分布式數據庫中自然而然會遇到事務的處理,2pc協議是分布式事務的一個解決方案;但是存在性能不夠好,單點故障的問題,宕機導致數據不一致的問題;實際使用中,常使用XA協議,采用2PC兩段式提交作為基礎,是X/Open DTP組織(X/Open DTP group)定義的兩階段提交協議。

 

高層應用對Rocksdb的封裝

1.MVCC

對於DBMS來說,常用到MVCC進行並發情況下的快速讀寫和隔離,MVCC會需要掃描同一個key下多個timestamp下的值,但是由於LSM的特性,SCAN操作的時候每個key可能出現在SSTable的任意層次,所以讀放大明顯。一般采用bloom filter降低IO讀寫次數,但是在長key造成很大的key空間的情況下,這種方法也捉襟見肘。CockroachDB采用了prefix 前綴bloom filter來緩解這個問題。

2.Backwards iteration

后向遍歷在其他key-value db上一般效率比forward iteration慢,但是Rocksdb有對其進行優化。

3.Graph Database

圖數據庫Dgraph利用Rocksdb作為(Predicate, Subject) --> PostingList的Key-Val存儲,即圖中的一個有向邊。其次,利用Rocksdb的單key隨機讀寫性能,建立PostingList的索引。

 

三、Rocksdb可以改進的問題

讀寫放大嚴重;

應對突發流量的時候削峰能力不足;

壓縮率有限;

索引效率較低;

scan效率慢;

References

https://cloud.tencent.com/developer/article/1441835深入理解LSM-Tree

https://zhuanlan.zhihu.com/p/49966056 看圖了解RocksDB

https://www.jianshu.com/p/8de55d5df05e LSM Compaction Strategy

https://docs.scylladb.com/kb/compaction/ Compaction

https://wanghenshui.github.io/2019/03/12/rocksdb-2pc-xa-transcation

http://alexstocks.github.io/html/rocksdb.html RocksDB 筆記

https://zhuanlan.zhihu.com/p/148941340 Rocksdb Secondary Instance啟發與實踐

https://www.cockroachlabs.com/blog/cockroachdb-on-rocksd/

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM