WiscKey:LSM-Tree 寫放大優化
第一次看完 WiscKey 這篇論文,覺得寫得很接地氣,很實用,很通俗易懂。
這裡簡單記錄一下。
WiscKey 簡介
WiscKey 的提出,主要是為了優化 LSM-Tree 的寫放大問題。此前已經有不少論文討論過這個問題,如 LSM-trie 和 PebblesDB,但是大部分優化方法都不是很徹底——簡單說就是,優化效果太差,或者不夠通用。WiscKey 提出的是一種比較通用、效果明顯且簡單易懂的方法。
其實 WiscKey 優化寫放大的原理非常簡單: 在 LSM-Tree 上做 key 和 value 的分離儲存 。
LSM-Tree 寫放大的根本原因是,compaction 時為了保證資料有序進行大量資料(key 和 value)重寫。實際上,需要保持有序的只有 key,如果將 key 和 value 分開儲存,compaction 重寫資料的時候,就只需要重寫 key(和 value 的位置,簡稱 vpos)。這在 key size (簡稱 ksize)遠小於 value size (簡稱 vsize)的場景(現實場景基本都是這樣)降低寫放大的效果十分明顯。
另一方面,當 vsize 比較小的時候,重寫 value 這部分的開銷就比較小,key-value 分離儲存帶來的好處就不足以抵消它帶來開銷。Key-value 分離儲存帶來的額外開銷 —— 讀寫一個 key-value 需要操作不同檔案。這在 range query 的場景下,會產生多次隨機 I/O,影響比較明顯。如下圖所示。

WiscKey-Range-Query-Performance.PNG
對順序 range query:
- 當 vsize 比較小的時候,LevelDB 的效能和 WiscKey 很接近。因為是順序 range query,WiscKey 的預讀快取功能可以有效地提升效能。
- 當 vsize 逐漸變大,LevelDB 的效能急劇下降,WiscKey 的效能比較平穩,明顯優於 LevelDB。因為 value 變大,導致 LevelDB 一個 block 可以儲存的 kv pair 變少了,cache 效果變差。
對於隨機 range query:
- 當 vsize 比較小的時候,LevelDB 的效能優於 WiscKey。因為是隨機 range query,範圍應該比較小,WiscKey 的預讀快取功能沒能很好發揮作用。
- 當 vsize 逐漸變大,WiscKey 的效能很快超越了 LevelDB。
Key-value 分離儲存後,LSM-Tree 索引結構上只儲存 key 和 vpos,實際的 value 儲存在另一個 append-only 的 log 檔案中,稱為 vlog。 總體看來,WiscKey 很像一個用 LSM-Tree 做索引的 BitCask。
WiscKey 帶來的好處
- LSM-Tree Compaction 不需要重寫 value,大大減小寫放大。
- LSM-Tree 不儲存 value,體積更小,一個 block 能存更多的 key,有利於減少讀 LSM-Tree 的 I/O。
- LSM-Tree 的體積小,cache 效果應該會更好。LSM-Tree 的上面幾層基本都可以 cache 在記憶體中。
WiscKey 面臨的問題和挑戰
雖然減小了寫放大,提升了 key 的密度,進而優化了 LSM-Tree 的 cache 效果。但是 key-value 分離儲存也給 WiscKey 帶來一些問題和挑戰。下面簡單分析一下。
-
當發起一次 range query 的時候,最終會轉化成多次針對 vlog 的隨機讀。這種場景下, WiscKey 會通過多個後臺執行緒對後面的多個數據進行預讀,並快取起來,充分利用 SSD 內部的並行性。預讀快取對於範圍比較大的 range query 效果可能比較明顯,對於小範圍的隨機 range query,可能發揮不了什麼作用。
-
Key 和 value 分開儲存後,怎麼保證 key 和 value 的一致性呢?針對這個問題,WiscKey 採用的方案是,先寫 vlog,再寫 LSM-Tree 。如果寫 vlog 成功,寫 LSM-Tree 失敗,則寫失敗。此時,vlog 上的值不會被暴露出去,只需後面進行垃圾回收。這裡如果要保證資料一致性和可靠性,則寫 vlog 後要刷盤,寫 LSM-Tree 後也要 WAL 刷盤。這裡有兩次刷盤開銷,通過 WAL 和 vlog 的合併,可以優化成一次。
-
vlog 的垃圾回收。垃圾回收需要掃描資料,根據資料結構,有兩個方向可以考慮:
- 掃 LSM-Tree。根據 LSM-Tree 把 vlog 中有效的內容都提取出來。這個方法不太適合線上服務,需要一次性重寫整個 vlog,開銷比較大。重寫 vlog 的同時外部依然會更新 LSM-Tree 和 vlog,資料一致性不太好處理。
- 掃 vlog。為了掃描 vlog,vlog 不能只儲存 <value>,需要儲存 <key, value>。根據掃出來的每一個 <key, value>,通過 LSM-Tree 驗證其有效性。為了不影響線上服務,WiscKey 每次取出 vlog 最舊的一部分資料(幾 MB),通過 LSM-Tree 進行驗證,過期則丟棄,有效則 append 到 vlog,並修改 LSM-Tree(類似執行一次寫入操作),並刪除這部分回收驗證過的資料,通過 fallocate() 釋放無效的檔案磁碟空間。
參考文件
- ofollow,noindex">WiscKey: Separating Keys from Values in SSD-conscious Storage
- LSM-trie: An LSM-tree-based Ultra-Large Key-Value Store for Small Data
- PebblesDB: Building Key-Value Stores using Fragmented Log-Structured Merge Trees