深入理解LSM-Tree
深入理解LSM-Tree
- 基礎(chǔ)概念
- compaction策略
- Size-tired compaction strategy(STCS)/Tiered
- leveled compaction
- Leveled-N
- Hybrid
- Time-Window
- 比較
- 工業(yè)實現(xiàn)
- leveldb
- RocksDB
- Write Stalls
- scyllaDB/cassandra
- hbase
- TiKV/Titan
- 學(xué)術(shù)研究
- Dostoevsky
- 參考鏈接
lsm-tree的背景、定義和適用場景本文不再詳述。本文意在論述LSM的存儲引擎、工業(yè)取舍以及發(fā)展現(xiàn)狀。
基礎(chǔ)概念
-
The on-disk data is organized into sorted runs of data. Each run contains data sorted by the index key. A run can be represented on disk as a single file, or alternatively as a collection of files with non-overlapping key ranges. To perform a query on a particular key to get its associated value, one must search in the Level 0 tree and each run.
- 可見,每一個run都是有序的數(shù)據(jù)塊。
compaction策略
LSM-Tree存儲引擎的工作流程如下:
從上面的存儲流程可以看出,LSM-Tree的性能主要取決于Compaction策略,它主要分為兩類,tiered compaction和leveled compaction
- tiered流派從Bigtable->HBase->Cassandra一脈相承,后面又有MyRocks、InfluxDB、Kudu等等
- Leveled流派工程實現(xiàn)主要有LevelDB->RocksDB
Size-tired compaction strategy(STCS)/Tiered
tiered適合write-intensive workload,有較低的寫放大,缺點是讀放大和空間放大較高。
tiered compaction的策略是:memtable達到閾值之后刷入下一層(落盤)作為一個run,每層的run達到一定數(shù)量之后,將當(dāng)前層所以的run進行compaction并刷入下一層,循環(huán)往復(fù)。這種策略給人直觀的感受是,越下層的run越大,當(dāng)然實際的策越要比描述的復(fù)雜,因為run與run之間重疊的部分是不確定的,有可能完全重疊,也有可能完全不重疊。
- 由于所有的上層level的數(shù)據(jù)可以直接追加寫入下層,所以寫放大比較低
- 由于每層都有多個run,且每個run中的數(shù)據(jù)可以重疊,所以點讀操作需要由上到下遍歷每一個run,讀放大較嚴(yán)重。更不要提range查詢肯定要合并每一層的每一個run的查詢結(jié)果,讀放大更嚴(yán)重。
- 由于每層有多個run,每個run都可能含有重疊數(shù)據(jù),所以空間放大較為嚴(yán)重。空間放大可以說是STCS最嚴(yán)重的問題了。Scylla的文章通過實驗論證了這個問題
- 每層的多個run需要先合并,才能寫入下層,因此有可能導(dǎo)致臨時空間較大。耗時也較長
- 這里討論一個特例time-series data。key是時間,幾乎是恒定遞增的,因此越是下層,run越是不重疊,寫入和查詢的策略都可以做對應(yīng)的優(yōu)化。
leveled compaction
leveled compaction和上面的STCS相比,降低了空間放大和讀放大,比較適合read-intensive workload,當(dāng)然這個也只是相對而言,如果真的是讀很多,不如用B+ Tree。
leveled compaction每層只維護一個run,按照key排序可以分為多個partition(SSTable file)。每層容量達到一定限制后,選擇某個sstable與上層merge。
- 由于每層只有一個run,所以降低了讀放大和空間放大。但是點查詢?nèi)匀挥锌赡軙闅v所有的run、范圍查詢必然會遍歷所以的run。
- 由于leveled compaction上層與下層merge的過程有可能涉及到上下層所有的數(shù)據(jù),可能造成上層run全量重寫,導(dǎo)致寫入放大,但是由于每個run可以分為多個partition(sstable),因此可以節(jié)約部分臨時空間。
Leveled-N
不同于leveled,Leveled-N允許每層多個Sorted run。Dostoevsky paper引入了一種Fluid LSM:允許最高層只有一個run,而其它層可以有多個run。
Hybrid
這里討論scylladb實現(xiàn)的Hybrid模式。主要體現(xiàn)在這篇文章,其希望推出一種模式,可以吸納tiered和leveled兩者的優(yōu)點。scylladb引入Hybrid主要為了優(yōu)化tiered糟糕的空間放大。我推測主要有以下幾點優(yōu)化:
Time-Window
這里主要講scylladb/Cassandra針對時序數(shù)據(jù)引入的Time-Window compaction,時序數(shù)據(jù)有以下特點:
這種數(shù)據(jù)狀況下,通過將不同的時間分配在不同的Time-Window中,將SSTable分配成了一個個time bucket,實現(xiàn)形式上采用tiered compaction,不同的time bucket不會合并(優(yōu)化讀操作)。由于overlap并不多,所以compaction壓力不大,空間放大也不大。由于數(shù)據(jù)分布較好,查詢也較方便。
比較
scylladb的文章總結(jié)了以下表格:
| Write-only | 2x peak space | 2x writes | Best | - |
| Overwrite(指同一個key多次update) | Huge peak space | write amplification | high peak space bug not like size-tiered | - |
| Read-mostly, few updates | read amplification | Best | read amplification | - |
| Read-mostly, but a lot of updates | read and space amplification | write amplification may overwhelm | read amplification | - |
| Time series | write,read,and space amplification | write and space amplification | write and read amplification | best |
工業(yè)實現(xiàn)
leveldb
顧名思義,levelDB使用leveled compaction,其CRUD流程如下:
levelDB代碼清晰,代碼量少,閱讀方便,不過缺少大規(guī)模工程應(yīng)用場景。
RocksDB
LSM-Tre工業(yè)級實現(xiàn),和levelDB相比,參數(shù)更多,優(yōu)化更多,使用更靈活,Features Not in LevelDB這篇文章介紹了RocksDB相對于levelDB所做的優(yōu)化,我選擇一些優(yōu)點列舉如下:
Write Stalls
RocksDB會在某種情況下限制前臺寫請求的速度,稱為Write Stalls。產(chǎn)生的原因不外乎因為某種原因,compaction的速率趕不上前臺write的速率了。這種現(xiàn)象如果持續(xù)下去,會產(chǎn)生以下結(jié)果:
這種情況下需要將前臺寫請求降速,直到compaction執(zhí)行到某種可以繼續(xù)高效處理前臺讀寫請求的程度。
一般情況下,Write Stall產(chǎn)生的原因有以下幾種可能:
scyllaDB/cassandra
scyllaDB/cassandra默認(rèn)采用tiered,可以配置為levled、Hybrid或Time-Window。只有Hybrid是ScyllaDB獨有。實現(xiàn)細(xì)節(jié)如上所述
hbase
HBase是一個分布式,版本化,面向列的開源數(shù)據(jù)庫(面向列族Column Family,在)、分布式持久化KV數(shù)據(jù)庫。HDFS(僅支持追加寫)為Hbase提供可靠的底層數(shù)據(jù)存儲服務(wù),MapReduce為Hbase提供高性能的計算能力,Zookeeper為Hbase提供穩(wěn)定服務(wù)和Failover機制,因此我們說Hbase是一個通過大量廉價的機器解決海量數(shù)據(jù)的高速存儲和讀取的分布式數(shù)據(jù)庫解決方案。Hbase適合存儲PB級別的海量數(shù)據(jù),雖然當(dāng)個請求是時延不低,但是可以通過并行請求增大吞吐,并且單個IO的時延下降并不多。
架構(gòu)和bigtable比較類似,通過key-range,將數(shù)據(jù)水平切分到多臺服務(wù)器上,數(shù)據(jù)持久化在HDFS中。這點和現(xiàn)代的應(yīng)用如tidb略有不同,其是將數(shù)據(jù)切分之后通過一致性協(xié)議存儲在多個RSM上,每個RSM的數(shù)據(jù)引擎為LSM-Tree
Hbase 技術(shù)細(xì)節(jié)筆記(上)
hbase在實現(xiàn)中,是把整個內(nèi)存在一定閾值后,flush到disk中,形成一個file,這個file的存儲也就是一個小的B+樹,因為hbase一般是部署在hdfs上,hdfs不支持對文件的update操作,所以hbase這么整體內(nèi)存flush,而不是和磁盤中的小樹merge update。內(nèi)存flush到磁盤上的小樹,定期也會合并成一個大樹。整體上hbase就是用了lsm tree的思路。
因為小樹先寫到內(nèi)存中,為了防止內(nèi)存數(shù)據(jù)丟失,寫內(nèi)存的同時需要暫時持久化到磁盤,對應(yīng)了HBase的HLog(WAL)和MemStore
MemStore上的樹達到一定大小之后,需要flush到HRegion磁盤中(一般是Hadoop DataNode),這樣MemStore就變成了DataNode上的磁盤文件StoreFile,定期HRegionServer對DataNode的數(shù)據(jù)做merge操作,徹底刪除無效空間,多棵小樹在這個時機合并成大樹,來增強讀性能。
TiKV/Titan
TiKV使用RocksDB作為存儲引擎,并且實現(xiàn)了Titan作為RocksDB 的高性能單機 key-value 存儲引擎插件。以支持大value(Blob),其核心特性在于支持將 value 從 LSM-tree 中分離出來單獨存儲,以降低寫放大。
其主要設(shè)計靈感來源于 USENIX FAST 2016 上發(fā)表的一篇論文 WiscKey。WiscKey 提出了一種高度基于 SSD 優(yōu)化的設(shè)計,利用 SSD 高效的隨機讀寫性能,通過將 value 分離出 LSM-tree 的方法來達到降低寫放大的目的。
Titan 適合在以下場景中使用:
學(xué)術(shù)研究
Dostoevsky
這篇論文詳細(xì)地分析了各種操作在tired和leveled不同策略下的i/o復(fù)雜度,使用了level num、相鄰level size比、entry數(shù)量、buffer size等一系列相關(guān)變量推導(dǎo)出不同操作具體的I/O代價公式和空間放大,對于point read還考慮了bloom filter帶來的影響,range query分了short和long兩種來分析。并且提出了優(yōu)化策略lazying leveling,是tiering和leveling的結(jié)合體,最大一層使用leveling其它層使用tiering,lazying適合包含update、point lookup和long range lookup的混合負(fù)載,tiering適合大多為update的負(fù)載,leveling適合大多為lookup的負(fù)載。通過控制merge頻率在tiering、leveling和lazying leveling幾種不同策略下轉(zhuǎn)換以適應(yīng)不同的workload,稱作fluid LSM((類似rocksdb的leveled-n)),并提出Dostoevsky,在執(zhí)行期間動態(tài)計算最優(yōu)配置以到達最大吞吐。
從作者的分析中可以看出compaction這個機制的復(fù)雜程度,要使用一套通用機制在不同場景下消耗最少的資源帶來最好的性能基本是不可能的,實際工業(yè)界實現(xiàn)中需要考慮更多的因素(cache、delete、任務(wù)拆分粒度、復(fù)用技術(shù)等),因此大部分系統(tǒng)使用多種策略多個參數(shù)用以適配不同場景。
參考鏈接
總結(jié)
以上是生活随笔為你收集整理的深入理解LSM-Tree的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 分布式块存储QoS限速算法介绍与实践以及
- 下一篇: LevelDB源码解读