論文筆記 [SOSP '17] PebblesDB, Building Key-Value Stores using Fragmented Log-Structured Me...
PebblesDB為了減少寫放大,同時又不影響讀的效率,提出了一種類似於skiplist的方式來建立LSM-Tree,叫做Fragmented Log-Structured Merge Trees (FLSM)。與rocksdb相比,大概減少了2.4-3倍的寫放大,以及6.7倍的write throughput。這篇的idea也比較好懂,用分割槽的方式來減少compaction,但又可以像skiplist那樣來檢索,讀效率也很高。
Problems
目前的LSM-Tree存在的寫放大在於,它會對每一層的sstable寫多次。在每一次compaction的時候,需要將上下兩層有overlap的sstable讀到記憶體,進行排序,然後再輸出。但是這個操作是多次的,頻繁的。當上一層又滿了之後,又需要重複一遍上述操作,第二層overlap的sstable,又被寫了一遍。
如上圖中level1的sstable被重複寫了3次。這就是寫放大的問題所在。而LSM很巧妙的解決了這個問題
Fragmented Log-Structured Merge Trees
這個名字也很直觀。據作者說,本來這篇文章一開始投的是Eurosys,結果被拒了,後來重寫了paper,才取了這個名字,寫成所謂的資料結構創新,PebblesDB是基於FLSM,然後才被sosp接收了。
FLSM的思想就是將每一層的sstables劃分邏輯上的區域(level0除外),每個局域內sstable之間是可以重疊的。給這個區域取一個名字叫Guard。同時每個Guard有一個對應的key。假設有兩個連續的Guard:G1:k1,G2:k2,那麼G1內的sstable的key就在[k1, k2)這個範圍內。
做了這樣的劃分之後,查詢某個key,是先對整個level的Guards進行二分查詢,找到某個Guard之後,再查這個Guard裡的所有sstable(像level0那樣)。是不是和skiplist很像?Guard的劃分也是隨機的,在插入的時候根據概率來確定是否需要開闢一個新的Guard。並且每一層的概率是不同的,上層稀疏,下層緊密。上一層已經有的Guard,在下一層也必須有。如下圖。
那麼FLSM是怎麼解決寫放大的呢?就在於compaction的時候,每一層的sstable的只需要寫一次。上一層的sstable需要合併到下層的時候,只需要將上層的sstable做合併排序,然後根據下層Guard的key做劃分,新增到不同的Guard中即可。當然這也有個例外,就是在最後一層的時候,因為沒有更下層的Guard來新增,只能合併做重寫。過程就如下圖
EVALUATION
按照上述的結構,FLSM大量減少了寫放大。讀的時候需要查詢單個Guard內的多個sstable(bloom filter優化),因為PebblesDB增加了單個sstable的大小,效果也不差。但是FLSM也有侷限性,即在做range query的時候,讀放大會很嚴重(所有level,每個guard中所有sstable)。但是Guard的數量是可以調整的,調成1的時候就和傳統的LSM實現一樣了。
還有一種情況就是在順序插入的時候,在這種workload下,所有sstable就沒有overlap,之前的LSM實現,可以直接將上層的sstable移到下層不需要做IO。而PebblesDB還是要按照下層Guard的key做劃分之後寫入,增加了IO。下圖為測試結果。
Reference
[1] PebblesDB: Building Key-Value Stores using Fragmented Log-Structured Merge Trees. SOSP ‘17