問哭自己lsm?索引原理深入剖析
lsm簡(jiǎn)析
lsm 更像是一種設(shè)計(jì)索引的思想。它把數(shù)據(jù)分為兩個(gè)部分,一部分放在內(nèi)存里,一部分是存放在磁盤上,內(nèi)存里面的數(shù)據(jù)檢索方式可以利用紅黑樹,跳表這種時(shí)間復(fù)雜度低的數(shù)據(jù)結(jié)構(gòu)進(jìn)行檢索。

而當(dāng)內(nèi)存數(shù)據(jù)到達(dá)一定閥值的時(shí)候則會(huì)將數(shù)據(jù)同步到一個(gè)新的磁盤文件上。此時(shí)寫入磁盤的方式是順序?qū)?,這也是為什么lsm寫入性能高的原因。
提問開始
打住,你說寫入性能高,但是我們知道內(nèi)存中的數(shù)據(jù)如果在處于正在同步到磁盤的過程中,如果此時(shí)有新數(shù)據(jù)的插入,則會(huì)帶來并發(fā)讀寫問題,要想解決就要給這片內(nèi)存區(qū)域加鎖了。加鎖會(huì)導(dǎo)致寫入過程阻塞,這樣性能會(huì)高嗎?
業(yè)界一般是這樣解決的,當(dāng)內(nèi)存到達(dá)某個(gè)閥值后,就將這片內(nèi)存標(biāo)記為可讀,然后新的數(shù)據(jù)插入將會(huì)寫到新的內(nèi)存區(qū)域,而舊的內(nèi)存因?yàn)槭侵蛔x的原因,便可以不加鎖的進(jìn)行同步到磁盤的過程。
再來思考,由于每次同步是生成一個(gè)新的磁盤文件,那么lsm是如何再多個(gè)磁盤文件范圍里進(jìn)行數(shù)據(jù)檢索的呢? 由于內(nèi)存容量有限,每次生成的磁盤文件必然不會(huì)過大,這樣會(huì)不會(huì)產(chǎn)生大量的小容量的磁盤文件?
我來回答下, 查找數(shù)據(jù)的時(shí)候是從多個(gè)磁盤文件中讀取數(shù)據(jù),然后對(duì)結(jié)果進(jìn)行合并,只取最新的數(shù)據(jù)。
這里已經(jīng)可以看到和b+tree比較明顯的區(qū)別了,b+tree是插入的時(shí)候進(jìn)行原地合并,而lsm則是讀取時(shí)進(jìn)行數(shù)據(jù)合并。
由于數(shù)據(jù)在內(nèi)存中是有序的,所以在寫入磁盤時(shí),也保證了每個(gè)小的磁盤文件是有序的。我們將這些小的磁盤文件稱作sstable。
但是這樣的設(shè)計(jì)還有沒有問題,如果僅僅保證sstable文件有序,不同sstable文件索引的范圍有重疊的話,我們查找一個(gè)值的時(shí)候就可能會(huì)在多個(gè)sstable文件里尋找,最差的情況可能要找所有的sstable文件,如圖:

有個(gè)索引范圍是1-1000的sstable,和值范圍為500-2000的sstable,當(dāng)我們查找600時(shí),無法一開始就知曉600在哪個(gè)sstable里。
因此,業(yè)界一般是這樣做,對(duì)多個(gè)小文件進(jìn)行合并,讓磁盤文件之間不再有覆蓋關(guān)系。

將索引范圍合并后,兩個(gè)sstable之間將不再重疊,便能快速檢索到 查詢的值所在的sstable了。
還沒完,剛才提到了合并sstable文件,合并既能讓sstable文件之間不會(huì)產(chǎn)生索引范圍覆蓋,又能減少大量小體積的sstable,但是在什么時(shí)候進(jìn)行合并呢?
如果在新增sstable時(shí)進(jìn)行合并,新增一個(gè)sstable,發(fā)現(xiàn)現(xiàn)有的sstable和和新增的sstable索引的范圍都有重合關(guān)系,是不是要將新增的sstable全部與現(xiàn)有的sstable進(jìn)行多路歸并排序,然后再生成新的一個(gè)或多個(gè)sstable。

這樣的效率真的會(huì)高嗎? 新增的索引體積是比較小的,如果新增一個(gè)比較小的數(shù)量級(jí)的sstable文件就去合并所有的sstable文件顯然是不合理的,并且由于新增的sstable體積小,產(chǎn)生較為頻繁,如果每次都全量合并將會(huì)導(dǎo)致磁盤io在較長(zhǎng)時(shí)間都處于一個(gè)比較高的值。
所以,最后業(yè)界的實(shí)現(xiàn)一般采用下面的多層次合并的方式。每一層的容量是上一層容量的10倍。

level0層 是標(biāo)記為可讀的那片內(nèi)存直接順序?qū)懭氪疟P形成的sstable 文件的集合,只有4個(gè)文件,注意由于level0是內(nèi)存直接寫入生成的,所以level0層索引范圍是有重合的,而其他層的索引范圍將不會(huì)有重合產(chǎn)生。
當(dāng)再有新的的sstable文件生成時(shí),那么新的sstable就會(huì)和當(dāng)前層有重合的sstable合并到下一層。

當(dāng)新增一個(gè)sstable時(shí),sstable的范圍是500 ~ 1000 ,那么這個(gè)范圍中l(wèi)evel0層有500 ~ 1000的sstable和300 ~ 1200的sstable都和新增的sstable有重合,所以需要將這3個(gè)sstable一起合并到下一層,而合并到下一層時(shí),發(fā)現(xiàn)上一層需要合并的索引范圍是500 ~ 1200,所以找出level1層中與此索引范圍有重合的sstable,即level1 中標(biāo)記為紅色的sstable,然后再與它們進(jìn)行合并產(chǎn)生新的sstable。
如果合并后發(fā)現(xiàn)當(dāng)前層的容量達(dá)到了某個(gè)閥值,那么就又會(huì)將當(dāng)前層的sstable繼續(xù)合并到一層,一般我們會(huì)限制一個(gè)最大的層數(shù),到達(dá)最大層數(shù)后就不再繼續(xù)合并了。
這樣多層滾動(dòng)合并的設(shè)計(jì)能很好的解決每次新的sstable產(chǎn)生可能引發(fā)的高磁盤io的情況,因?yàn)樗鼘⒅暗囊淮涡院喜磳哟畏謹(jǐn)偟搅硕啻?,將整個(gè)合并過程分?jǐn)偟搅瞬煌臅r(shí)間段,緩解了寫放大問題。
lsm 小結(jié)
從lsm的實(shí)現(xiàn)上來看,已經(jīng)能夠明白它的一個(gè)數(shù)據(jù)寫入和檢索過程。這里再來總結(jié)一下。 lsm 寫入時(shí),會(huì)先寫入到內(nèi)存,內(nèi)存里數(shù)據(jù)的檢索一般是比較高效的數(shù)據(jù)結(jié)構(gòu),類似跳表,紅黑樹等,內(nèi)存中的數(shù)據(jù)是有序。 內(nèi)存到達(dá)某個(gè)閥值后,會(huì)將這片內(nèi)存標(biāo)記為只讀,后續(xù)新的寫入將在新的內(nèi)存區(qū)域上進(jìn)行,而只讀的內(nèi)存會(huì)將有序的數(shù)據(jù)寫入到磁盤level0層,形成sstable文件。當(dāng)level0層的sstable文件超過4個(gè)后,將會(huì)與level1層sstable產(chǎn)生合并行為,level0層以后的層級(jí)的索引范圍都是沒有重合的。
lsm讀取數(shù)據(jù)時(shí),同樣先從內(nèi)存中讀取,如果讀取不到則會(huì)從磁盤由低層到高層進(jìn)行讀取,讀取到則返回,讀取不到則直至最后一層為止。由于level0層以后的 每層 sstable數(shù)據(jù)都是有序且不重合的,在快速檢索到數(shù)據(jù)所在的sstable 后,便能快速通過二分查找判斷數(shù)據(jù)是否在該層中,真實(shí)實(shí)現(xiàn),在sstable還用上了布隆過濾,來快速判斷元素不在sstable的情況。如果該層找不到,則繼續(xù)往下一層尋找。
可以看到,在讀取數(shù)據(jù)時(shí),最差的情況要遍歷所有的層次,這也是為什么說lsm適合寫多讀少的場(chǎng)景,在讀時(shí)也最好讀取最近的數(shù)據(jù)。
看看與b+tree的區(qū)別
b+tree的索引更新是原地更新,原地更新帶來的代價(jià)很明顯,第一個(gè)是要加鎖,第二個(gè)由于更新時(shí)各個(gè)節(jié)點(diǎn)之前的在磁盤位置并不相鄰帶來的隨機(jī)寫入問題。 但b+tree的隨機(jī)讀性能很好,上千萬的數(shù)據(jù)最多也只需要兩三次磁盤io。
而lsm在高效寫的優(yōu)勢(shì)下 帶來了讀放大問題,最壞的情況可能要在lsm多層磁盤索引結(jié)構(gòu)中,每個(gè)層次都找一遍。在寫頻繁的場(chǎng)景下,查詢也基本上是查最近數(shù)據(jù)時(shí),lsm具有很好的性能。
問了一通之后,算是理清楚了lsm的原理了,平時(shí)我也傾向于向自己發(fā)問來不斷剖析問題,結(jié)尾我再問一個(gè)問題吧,這篇文章里,我一共問了幾個(gè)問題呢?
以上就是問哭自己lsm 索引原理及剖析的詳細(xì)內(nèi)容,更多關(guān)于lsm 索引原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳細(xì)聊聊關(guān)于sql注入的一些零散知識(shí)點(diǎn)
SQL注入攻擊是通過將惡意的SQL查詢或添加語句插入到應(yīng)用的輸入?yún)?shù)中,再在后臺(tái)SQL服務(wù)器上解析執(zhí)行進(jìn)行的攻擊,它目前是黑客對(duì)數(shù)據(jù)庫(kù)進(jìn)行攻擊的最常用的手段之一,這篇文章主要給大家介紹了關(guān)于sql注入的一些零散知識(shí)點(diǎn),需要的朋友可以參考下2021-10-10
數(shù)據(jù)庫(kù)設(shè)計(jì)規(guī)范化的五個(gè)要求 推薦收藏
通常情況下,可以從兩個(gè)方面來判斷數(shù)據(jù)庫(kù)是否設(shè)計(jì)的比較規(guī)范。一是看看是否擁有大量的窄表,二是寬表的數(shù)量是否足夠的少。2011-04-04
GaussDB數(shù)據(jù)庫(kù)何創(chuàng)建修改數(shù)據(jù)庫(kù)和數(shù)據(jù)表的方法
GaussDB 是一款由華為開發(fā)的企業(yè)級(jí)分布式數(shù)據(jù)庫(kù),具有高性能、高可用、高可靠性等特點(diǎn),廣泛應(yīng)用于各種業(yè)務(wù)場(chǎng)景,本指南將介紹如何在 GaussDB 中創(chuàng)建數(shù)據(jù)庫(kù)和數(shù)據(jù)表,修改表結(jié)構(gòu),并添加約束,需要的朋友可以參考下2024-06-06
數(shù)據(jù)庫(kù)sql查詢性能優(yōu)化詳解
這篇文章主要介紹了數(shù)據(jù)庫(kù)sql查詢性能優(yōu)化詳解,查詢優(yōu)化的本質(zhì)是讓數(shù)據(jù)庫(kù)優(yōu)化器為SQL語句選擇最佳的執(zhí)行計(jì)劃,對(duì)于大型的應(yīng)用系統(tǒng),大量的數(shù)據(jù)當(dāng)然需要效率最快的執(zhí)行語句,需要的朋友可以參考下2023-07-07
關(guān)于關(guān)系數(shù)據(jù)庫(kù)如何快速查詢表的記錄數(shù)詳解
這篇文章主要給大家介紹了關(guān)于關(guān)系數(shù)據(jù)庫(kù)如何快速查詢表的記錄數(shù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用關(guān)系數(shù)據(jù)庫(kù)具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
數(shù)據(jù)庫(kù)設(shè)計(jì)經(jīng)驗(yàn)談
這篇文章主要介紹了數(shù)據(jù)庫(kù)設(shè)計(jì)經(jīng)驗(yàn)談的相關(guān)資料,需要的朋友可以參考下2007-03-03
舉例簡(jiǎn)單介紹PostgreSQL中的數(shù)組
這篇文章主要介紹了舉例簡(jiǎn)單介紹PostgreSQL中的數(shù)組,PostgreSQL是一個(gè)高性能關(guān)系型數(shù)據(jù)庫(kù),學(xué)習(xí)PostgreSQL將成為趨勢(shì),需要的朋友可以參考下2015-04-04
數(shù)據(jù)庫(kù)分庫(kù)分表是什么,什么情況下需要用分庫(kù)分表
這篇文章主要介紹了數(shù)據(jù)庫(kù)分庫(kù)分表是什么,什么情況下需要用分庫(kù)分表,需要的朋友可以參考下2021-03-03
RBAC權(quán)限模型_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了RBAC權(quán)限模型,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08

