Rocksdb的優劣及應用場景分析
轉載:https://www.jianshu.com/p/73fa1d4e4273
研究Rocksdb已經有七個月的時間了,這期間閱讀了它的大部分程式碼,對底層儲存引擎進行了適配,同時也做了大量的測試。在正式研究之前由於對其在本地儲存引擎這個江湖地位的膜拜,把它想象的很完美,深入摸索之後才發現現實很骨感,光鮮背後都有不為人知的辛酸苦辣。同時這也給幻想追求完美技術的我打了一針清醒劑,任何東西都是兩面性的,沒有好與壞,只有適合和不適合,世界就是這麼殘酷,多麼痛的領悟!
Rocksdb也是一樣,也有它的優勢劣勢及特定的適用場景。今天我就從設計的角度來分析一下。
基礎架構
上圖就是Rocksdb的基礎架構。Rocksdb中引入了ColumnFamily(列族, CF)的概念,所謂列族也就是一系列kv組成的資料集。所有的讀寫操作都需要先指定列族。寫操作先寫WAL,再寫memtable,memtable達到一定閾值後切換為Immutable Memtable,只能讀不能寫。後臺Flush執行緒負責按照時間順序將Immu Memtable刷盤,生成level0層的有序檔案(SST)。後臺合併執行緒負責將上層的SST合併生成下層的SST。Manifest負責記錄系統某個時刻SST檔案的檢視,Current檔案記錄當前最新的Manifest檔名。 每個ColumnFamily有自己的Memtable, SST檔案,所有ColumnFamily共享WAL、Current、Manifest檔案。
架構分析
整個系統的設計思路很好理解,這種設計的優勢很明顯,主要有以下幾點:
1.所有的刷盤操作都採用append方式,這種方式對磁碟和SSD是相當有誘惑力的;
2.寫操作寫完WAL和Memtable就立即返回,寫效率非常高。
3.由於最終的資料是儲存在離散的SST中,SST檔案的大小可以根據kv的大小自由配置, 因此很適合做變長儲存。
但是這種設計也帶來了很多其他的問題:
1.為了支援批量和事務以及上電恢復操作,WAL是多個CF共享的,導致了WAL的單執行緒寫 模式,不能充分發揮高速裝置的效能優勢(這是相對介質講,相對B樹等其他結構還是有優 勢);
2.讀寫操作都需要對Memtable進行互斥訪問,在多執行緒併發寫及讀寫混合的場景下容易形 成瓶頸。
3.由於Level0層的檔案是按照時間順序刷盤的,而不是根據key的範圍做劃分,所以導致各 個檔案之間範圍有重疊,再加上檔案自上向下的合併,讀的時候有可能需要查詢level0層的 多個檔案及其他層的檔案,這也造成了很大的讀放大。尤其是當純隨機寫入後,讀幾乎是 要查詢level0層的所有檔案,導致了讀操作的低效。
4.針對第三點問題,Rocksdb中依據level0層檔案的個數來做前臺寫流控及後臺合併觸發, 以此來平衡讀寫的效能。這又導致了效能抖動及不能發揮高速介質效能的問題。
5.合併流程難以控制,容易造成效能抖動及寫放大。尤其是寫放大問題,在筆者的使用過程中實際測試的寫放大經常達到二十倍左右。這是不可接受的,當前我們也沒有找到合適的解決辦法,只是暫時採用大value分離儲存的方式來將寫放大盡量控制在小資料。
適用場景
1.對寫效能要求很高,同時有較大記憶體來快取SST塊以提供快速讀的場景;
2.SSD等對寫放大比較敏感以及磁碟等對隨機寫比較敏感的場景;
3.需要變長kv儲存的場景;
4.小規模元資料的存取;
不適合場景
1.大value的場景,需要做kv分離;
2.大規模資料的存取