區塊鏈江湖第1彈--LSM Tree

譯文

探索.jpeg

自從谷歌發表了他的”big table“論文以來已經過去了十年。論文中很酷的一點是它使用的文件組織方式。在1996年后,這個方式以LSM樹(Log-Structure Merge Tree)被人所熟知。
LSM現在被用于很多產品的主流文件組織策略。HBase,Cassandra,LevelDB,SQLite,甚至是MongoDB收購了Wired Tiger引擎后,其3.0附帶了一個LSM引擎的選項。
使LSM樹有趣的是他們離開二叉樹風格文件組織主導的領域已經有十幾年的歷史了。當你第一次看到LSM,它看起來幾乎是違反人類思維的,然而當你考慮到文件是如何工作在現代笨重的存儲系統上一切就說的通了。

背景知識

二分查找:又稱折半查找,優點是比較次數少,查找速度快,平均性能好;其缺點是要求待查表為有序表,且插入刪除困難。因此,二分查找方法適用于不經常變動而查找頻繁的有序列表。首先,假設表中元素是按升序排列,將表中間位置記錄的關鍵字與查找關鍵字比較,如果兩者相等,則查找成功;否則利用中間位置記錄將表分成前、后兩個子表,如果中間位置記錄的關鍵字大于查找關鍵字,則進一步查找前一子表,否則進一步查找后一字表。重復上述過程,直到找到滿足條件的記錄,使查找成功,或直到子表不存在位置,此時查找不成功。
哈希(HASH):(散列)函數,是將任意長度的數據映射到有限長度的域上。直觀解釋來說,就是對一串數據a進行雜糅,輸出另一端固定長度的數據h,作為這段數據的特征(指紋)。也就是說,無論數據塊a有多大,其輸出值h為固定長度。到底是什么原理?將a分成固定長度(如128位),依次進行hash運算,然后用不同的方法迭代即可(如前一塊的hash值與后一塊的hash值進行異或)。如果不足128位怎么辦?用0補全或1補全隨意,算法中的約定好就可以。

由于用途的不同,hash在數據結構中的含義和密碼學中的含義并不相同,所以在這兩種不同的領域里,算法的設計側重點也不同。
預備小知識:
抗碰撞能力:對于任意兩個不同的數據塊,其hash值相同的可能性極小;對于一個給定的數據塊,找到和它hash值相同的數據塊極為困難。
抗篡改能力:對于一個數據塊,哪怕只改動其一個比特位,其hash值的改動也會非常大。在用到hash進行管理的數據結構中,比如hashmap,hash值(key)存在的目的是加速鍵值對的查找,key的作用是為了將元素適當的放在各個桶里,對于抗碰撞的要求沒有那么高。換句話說,hash出來的key,只要保證value大致均勻的放在不同的桶里即可。但整個算法的set性能,直接與hash值產生速度有關,所以這個時候的hash值的產生速度就尤為重要。
在密碼學中,hash算法的作用主要是用于消息摘要和簽名,換句話說,他主要用于對整個消息的完整性進行校驗。舉個例子,登陸簡書的時候都需要輸入密碼,那么簡書如果明文保存這個密碼,那么黑客就很容易竊取大家的密碼來登陸,特別不安全。那么簡書就想到了一個方法,使用hash算法生成一個密碼的簽名,簡書后臺只保存這個簽名值。由于hash算法是不可逆的,那么黑客即便是得到這個簽名,也絲毫沒有用處;而如果你在網站登陸界面上輸入你的密碼,那么簡書后臺就會校驗你密碼生成的簽名值是否匹配。如果匹配,證明你擁有這個賬戶的密碼,那么就允許你登陸。銀行也是如此,銀行是萬萬不敢保存用戶密碼原文的,只會保存密碼的hash值而已。
在這些應用場景里,對于抗碰撞和抗篡改能力要求極高,對速度的要求在其次。一個設計良好的hash算法,其碰撞能力是很高的。以MD5為例,其輸出長度為128位,設計預期碰撞概率為1/2的64次方,這是一個極小的數字,而即便是在MD5被王小云教授破解之后,其碰撞概率上限也高達1/2的41次方。
在密碼學中,hash算法有不少有意思的改進思路,以應付不同的使用場景。例如變色龍Hash(ChameleonHash),它有一個有趣的特性。在普通情況下,ChameleonHash可以當作普通的hash算法使用,從明文(用a表示)得到hash值(用h表示)抗碰撞能力依然特別強;但是如果使用者在計算這個hash值時預先計算一個值(用s表示)并保存,那么通過這個值很容易計算出另一個hash值也為h的明文 a!也就是說,如果你保留這個值的話,hash算法的抗碰撞能力完全被解除了。這意味著,如果某個網站想要作惡的話,那么他可以很容易的替換他們自己的hash算法ChameleonHash,方便地偽造出一個密鑰來竊取用戶的所有數據,而這個公司完全可以在對外宣傳的時候,依然聲稱對用戶信息嚴格保密--《教網站如何優雅的耍流氓》。

B+樹:是應文件系統所需而出的一種B-樹的變型樹,通常在B+樹上有兩個頭指針,一個指向根結點,一個指向關鍵字最小的葉子結點。

1.有n顆子樹的結點中含有n個關鍵字,每個關鍵字不保存數據,只用來索引,所有數據都保存在葉子結點上。
2.所有的葉子結點中包含了全部關鍵字的信息,及指向這些關鍵字記錄的指針,且葉子結點本身依關鍵字的大小自小而大順序鏈接。
3.所有的非終端結點可以堪稱是索引部分,結點中僅含其子樹(根結點)中的最大(或最小)關鍵字。

ISAM(帶索引序列存取法)(Indexed Sequential Access Method縮寫)是IBM公司發展起來的一個文件操作系統,可以連續的(按照他們進入的順序)或者任意的(根據索引)記錄任何訪問。每個索引定義了一次不同排列的記錄。一個職工數據庫基于搜尋的信息可以有幾個索引。例如,按照職工所屬科室的部門索引中,同時還有按照職工姓氏字母順序排名的名字索引。每個索引中的關鍵字都是制定的。對于職工名字字母順序索引,姓就是指定的關鍵詞。

簡而言之,LSM樹被設計來提供比傳統的B+樹和ISAM更好的寫入性能。他通過消除隨機的就地更新操作來實現這一點。
所以為什么這是一個好主意呢?其核心是磁盤的老問題:隨機操作緩慢,但當順序訪問時很快。這一點分歧在兩種不同的訪問方式中間,不管磁盤是固態還是磁性材料,盡管這種影響在主存較小。
順序操作在主存上比隨機操作更快。他們也進一步表明了,在磁性或固態硬盤上,順序操作比起隨機操作快上三個數量級。這意味著隨機操作應當被避免,順序操作很值得設計。

隨機操作、順序操作.jpg

一個小猜想實驗:如果我們對吞吐量有興趣,什么是最好的使用方法呢?一個好的出發點是鑒定的追加數據到文件里。這種方式經常被叫做日志,檔案或者堆文件,是完全順序化的,因此能提供非常快的相當于理論硬盤速度的寫性能(通常平均200~300MB/S)
受益于基于簡單性和高性能的日志方法正在許多大數據工具中變的十分受歡迎。然而他們有一個很明顯的缺點。從日志中讀任意數據會消耗遠多于寫入時間,這當中包括了反向掃描,直到找到需要的關鍵字。
這意味著日志只適用于數據作為整體被訪問的簡單情況,例如大部分數據庫的預寫日志(WAL,write-ahead logging)或者通過一個已知的偏移量來訪問。簡單的消息系統Kafka就是這么做的。
所以我們需要不只是日志這樣的方式來高效執行更多的復雜讀工作,例如基于關鍵字的訪問或范圍訪問。一般來說,有四種方法在這種情況下是有用的:二分查找,哈希,B+或者外部文件。
1.二分查找:保存數據到文件的時候以關鍵字排序。如果數據已經確定寬度就是用二分查找,否則使用頁標簽+檢索。
2.哈希:用哈希函數把數據分片,然后可以直接訪問讀取。
3.B+:使用文件組織如B+樹,ISAM等等
4.外部文件:除數據本身按日志形式存儲外,另對其單獨建立索引加速讀取。
所有這些方式顯著提升了讀性能(大部分是n->O(log(n))復雜度的)。可這些結構加入順序,而這些順序阻礙了寫入性能。所以我們的高速日志文件方式被放棄了。魚和熊掌不可兼得。

值得注意的是,上述四個選項利用了數據的某種總體結構。數據被謹慎和特殊設計后放在文件系統上以便于能迅速的再次訪問到。這種結構使得訪問很迅速。這樣的結構在寫入數據后是很好的。我們開始通過增加磁盤隨機訪問來減少寫性能。
有幾個具體問題。對每次寫都有兩次IO要求,一次是讀頁面一次是寫回這個頁面。這不是我們的日志方式可以勝任的情況。
更糟的是,當我們需要更新哈希或者B+索引結構時,這意味著更新文件系統的特定部分。已知的例如就地更新操作需要緩慢的隨機IO。有一點很重要:在文件系統執行的更新像機關槍一樣多。這是瓶頸。
一個常見的解決方法是使用方法四對日志建立索引,然而把索引保存在內存中。例如一個哈希表能用日志文件的最后一個值的位移量來映射關鍵字。這種方法的確很好用,他把隨機IO劃分成較小的段,然后在內存中去取鍵-位移量的映射。查找一個值就只需要在磁盤做一次IO了。
然而另一方面這種方法存在擴展性限制,特別是當你有很多小值,如果你的值只是一些簡單的數字,那么索引將比數據文件本身還大。從Riak到Oracle Coherence的很多產品已經對這個缺陷妥協了。
所以這給我們帶來了LSM樹。LSM樹帶來了以上四種方法不同的方式。他能成為磁盤的中樞,只需要內存空間一點地方來提高效率,但是也使用一個簡單的日志文件來保證寫入性能。一個缺陷是他的讀性能比起B+樹來說還是稍微弱了些。
本質上它盡可能的使磁盤順序訪問。這里再也沒有機槍訪問了。
許多不需要就地更新的操作樹存在著。最受歡迎的就是append-only Btree,也被叫做copy-on-write樹。他通過每次寫操作順序的發生在文件末尾來覆蓋原先的樹結構。包括頂節點在內的老的樹結構的一部分成為孤兒節點。盡管這種方式避免了以數樹結構依次的重新構造本身,然而這樣的操作本身存在成本。重寫這些結構的每次寫是冗長的,他的缺點是帶來了大量的寫操作。

基礎的LSM算法

概念上LSM樹是非常簡單的,不同于有一個大的索引結構來存下批量的寫操作(像機關槍一樣在磁盤中掃描或者增加大量的寫操作)。他順序的,寫入到一系列的更小的所有文件中。所以每個文件包含了一批在一段短時間內的變化。每個文件在被寫入前都被排序以便于稍微快速的檢索。這些文件都是不變的;他們從來不更新。每次更新都寫入新的文件。會檢查所有文件來定期合并,以降低文件的數量。
讓我們來看看一些細節吧。當更新到達時,他們被添加到一個內存緩沖區,通常是作為一棵樹(紅黑等等)來保證鍵值順序。這”內存表“在大部分實現里以預寫日志(WAL)的方式復制到磁盤中,以用作恢復。當排序的數據充滿了內存表,會被刷入硬盤的一個新文件。這樣的過程隨著越來越多的寫入不斷重復著。重要的文件無法被編輯,所以系統制作順序IO。新的條目或者簡單的編輯會建立新的文件。


LSM寫操作.png

隨著越來越多的數據進入系統,越來越多的不可變的有序文件被創建。每個代表了一段時間內數據變化的排序集和。舊文件不會去更新重復的條目來取代先前的記錄(或被刪除的記錄)。隨意開始出現一些冗余。
系統定期的執行壓縮操作。壓縮操作把很多文件合并起來,消除其中重復的更新或者刪除操作(更多的是當第一步操作完成以后)。這對于消除上面提到的冗余問題非常重要,但是更重要的是,通過減少文件的增長量來保證讀操作的性能。值得慶幸的是,由于文件被排序了,所以合并文件的過程是相當高效的。
當請求讀操作時,系統首先檢查緩沖區(內存表)。如果這些關鍵字沒找到,那么會一個接著一個反序檢查一系列磁盤文件,直到關鍵字被找到。每個文件都被順序排列所以很容易遍歷,然而當文件越來越多的時候,會變得越來越慢,這是因為需要檢查每一個文件。這是一個問題。
所以光看讀性能,LSM比他的就地修改的同胞慢一些。幸運的是有一些技巧可以提高性能。最常見的方法是在內存建立頁索引,這是提供了一個讓你更接近目標關鍵字的查找。你在內存中的檢索數據是被排序過了的。LevelDB,RocksDB和Big Table對每個文件的末尾做塊索引來實現這一點。這通常比直接二分搜索要好一些,因為它允許可變長字段的用戶以及更適合壓縮后的數據。

即使是每個文件按被索引了,讀操作依然會隨著文件的增長而變得緩慢。讀操作被定期的合并文件來保證性能。這些合并操作控制了文件的數量,使得讀性能在一個可接受的范圍內。
即使有壓縮操作,讀操作依然會訪問多個文件。大部分實現通過布隆解析器(Bloom filter)來避免這一點。布隆解析器是一個使用內存判斷文件是否包含一個關鍵字的有效方法。


Bloom filter.jpg

所以從寫的角度來看,所有的寫操作被成批的寫入連續的塊中。并且存在一個額外的,周期性的壓縮合并操作。然而讀操作在尋找單個數據時有可能接觸大量的文件(也就是機槍讀法)。這是簡單算法的工作方式。我們隨機寫來交換隨機讀。如果我們能使用軟件技巧如布隆解析器或者硬件技巧如大文件緩存來優化讀取性能,這樣的交換是明智的。

基本壓縮法

控制文件的數量來保證LSM讀取的相對較快是很重要的,所以我們更加深入了解壓縮方法。這個過程有點像分代的垃圾收集:
當創建一定數量的文件,比如5個文件,每個有10行,他們合并成一個單獨的50行文件(或者稍微少些)。
這樣的過程隨著10行的文件被創建而不斷進行著。這些文件每5個文件填滿后合并成50行文件。
最終有5個50行文件。此時5個50行文件合并成一個250行文件。這個過程將持續著創建越來越大的文件。
這種方法所帶來的上述問題是大量的文件會被創建:為了獲取最終的結果所有文件都必須被遍歷搜索(至少在最壞的情況下)。

分級壓縮法

更加新潮的實現,例如LevelDB,RocksDB和Cassandra中,通過實現分級而不是大小來解決這個問題。這減少了最差情況下大量文件的讀取,同樣減少了單個壓縮的(對整個數據集的)相對沖擊。
這種分級法比起基本方法有以下兩個不同關鍵點:
1.每一層能包含大量的有保證的文件,作為一個整體,不會操作重復的關鍵字。也就是說關鍵字被分片在可用文件中。因此在某個層級尋找一個關鍵字只需要查詢一個文件。
第一級是一個以上性質不存在的特例。關鍵字可以跨不同的文件中。
2.文件一次被合并成高層級的一個文件。當一層滿了,一個文件會從上抽離,并且合并進入上一級來創建加入更多數據的空間。這比起基本方法中,把相似大小的文件合并誠一個單獨的較大文件,略有些不同。
這些變化意味著基于層級的方法隨著時間的推移來遍及壓縮的影響,需要更少的整體空間。他也有更好的讀性能。然而大部分的工作負載的IO會變高,這意味著對一些更簡單的以寫為目的的工作情況,并沒有幫助。

總結

所以LSM樹在日志文件方法和例如B+或者哈希索引這樣的傳統的單索引方法之間取得了均衡。他提供了一種機制來管理一組更小的獨立索引文件。
通過管理一組索引而不是單獨一個,LSM方法把與B+或哈希索引中開銷較大的隨機IO替換為快速的順序IO。
讀操作不得不處理大量的索引文件而不只是一個,是這樣的改變需要付出的代價。同時也增加了壓縮操作的IO成本。
如果講的還不清楚,那么這里有不錯的描述:
leveldb
leveled-compaction-in-apache-cassandra

LSM方法的思考

所以是否LSM方法真的比傳統的單樹方法要好呢?
我們已經看到,LSM有更好的寫性能,雖然有一些開銷。但是LSM有其他方面的好處。被LSM樹創建的SSTables(被排序的文件)是不能被修改的。這使得鎖定予以容易實現的多。通常認為內存表是唯一的競爭資源。這比需要構造精巧的鎖機制來管理變化的單樹結構要好很多。所以最終的問題是如何滿足以寫為主的情況。如果你關心寫性能,那么除了LSM方法以外可能都是一個大問題。大型互聯網公司看起來相當看重LSM。例如雅虎,報告在駕馭增長的事件日志和移動數據時,從大量的讀負載操作(read -heavy)穩步向讀和寫的負載(read-write)上演變。而許多傳統數據庫依然提供了偏于更多的優化讀的文件結構。
因為日志結構的文件系統的關鍵參數在于增加內存。通過操作系統提供的文件緩存,更多的可用內存自然能優化讀的性能。寫性能(當內存不增加的情況下)因此成為了短板。所以換句話說,硬件的提升對讀性能比寫性能提升的更多。
因此選擇一個寫為主要目的的文件結構就說的通了。
當然諸如levelDB和Cassandra的實現通常比單樹的方法提高了更好的寫性能。

超越分層LSM方法

在LSM方法的基礎上有更多的工作。雅虎開發了叫Pnuts的系統把LSM和B樹結合并且演示出更好的性能。
硬件的使用方式也是值得去考慮的。昂貴的固態磁盤,例如FusionIO,有更好的隨機寫性能。這適合就地更新方法。稍微便宜的SSD和機械磁盤更適合考慮LSM。LSM避免了少量的隨機存取把SSD弄廢。
LSM也不是全無被批評。他的最大問題,例如全局緩存,在收集階段對寶貴IO有影響。
所以如果你看數據產品,用BDB對比LecelDb,用Cassandra對比MongoDb。你要把他們的文件結構和他們的相關性能練習在一起(比如LSM適合寫多讀少,因此ldb和cassan適合寫多讀少)。測試出來的數據支持這種看法,你使用的系統(以及場合)當然要考慮到系統選擇的算法而造成的性能折扣。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容