The Log-Structured Merge-Tree(譯):中
http://duanple.blog.163.com/blog/static/7097176720123202125771/
3.3 Multi-Component LSM-Trees
對於給定的LSM-tree,引數M代表了rolling merge過程中插入到每個C1樹的葉子節點中的C0樹的平均記錄數。在merge到C1樹的節點中之前,這些新記錄會首先在C0中積累一段時間,因此通常我們認為M是大於1的。但是,通過公式(3.2){! M=(Sp/Se)·(S0/(S0+S1))}能夠看出,如果與C0樹相比C1樹足夠大,或者是單條記錄非常大以至於單個page中只能放下很少的記錄,那麼M的值就可能會小於1。這樣的一個M值意味著,為了能將C0中的一條記錄移出記憶體將不得不讀入多個C1的page。根據公式(3.4){! COST(LSM-ins)/ COST(B-ins)=K1·(COSTπ/COSTp)·(1/M)},在M< K1·(COSTπ/COSTp)的情況下,將會抵消掉multi-page的批處理效果,此時對於插入操作來說使用B-樹要比使用LSM-tree更划算。
為了避免得到一個太小的M值,對於兩元件LSM-tree來說,只能通過增加C0對C1的相對大小來解決。考慮一個葉子節點總大小為S(S=S0+S1,一個近似穩定的值)的兩元件LSM-tree,同時假設新節點以一個不變的速率R(以bytes/秒為單位)插入到C0中。為簡化起見,我們假設在被移入到C1中之前,沒有記錄會被刪除,因此這些記錄也必須以與它們被插入到C0中的相同的速率被移入到C1,以保證C0的值總是維持在閾值大小附近。(由於總大小S基本上是不變的,這也暗示著與插入到C0的速率相比,也需要一個與之平衡的從C1中進行刪除的速率,比如可能使用了斷言式刪除)。當我們改變C0的大小時,也會影響到merge遊標的迴圈週期。由於到C1的移出速率(每秒的位元組數)需要保持不變,這樣就要求遊標每秒所掃描的資料保持不變,在C0大小減少時,很明顯遊標從最小值到最大值的掃描頻率必須要加快;這樣就導致C1中用於rolling merge過程的multi-page block IO也必須要加快。極端情況下,比如C0大小隻能存下一條記錄,這樣對於每個新插入的記錄,都需要完整地將C1掃描一遍,這將產生大量的IO需求。此時,這種將C0和C1進行merge的方式,與B-樹中為每個新插入記錄只需訪問相關節點的方式相比,將會帶來更大的效能瓶頸。與之相比,C0越大將越能增大merge遊標的掃描週期,從而降低插入所需的IO開銷。但是,這也會增加所需的記憶體開銷。
通常情況下,可以通過最小化LSM-tree的總開銷(用於C0的記憶體開銷加上用於C1的磁碟空間/IO能力開銷)來確定C0的大小。為了達到這種最小化的開銷,我們通常從一個比較大的C0元件開始,同時讓C1元件大小接近於所需空間的大小。在C0元件足夠大的情況下,對於C1的IO壓力就會很小。現在,我們可以開始試著通過減少C0的大小,來在昂貴的記憶體和廉價磁碟之間進行權衡,直到減低到當前C1所能提供的IO能力完全被利用為止。此時,如果再降低C0的記憶體開銷,將會導致磁碟儲存開銷的增加,因為需要擴充C1元件以應對磁碟負載的增加,最終將會達到一個最小開銷點。現在,對於兩元件LSM-tree來說,這個典型的C0元件大小從記憶體使用的角度上看開銷仍是比較高的。一個改進的方案是,採用一個三元件或者多元件LSM-tree。簡單來說,如果C0太大以至於記憶體開銷成為主要因素,那麼我們可以考慮在兩元件LSM-tree的C0和C1之間加入一箇中間大小的基於磁碟的元件,這就允許我們在降低了C0大小的同時還能夠限制住磁碟磁臂的開銷。
通常,一個具有K+1個元件的LSM-tree具有元件C0,C1,C2…,Ck-1和Ck,元件大小依次遞增;C0元件是基於記憶體的,其他都是基於磁碟的(對於那些經常訪問的頁面來說會被快取在記憶體中)。在所有的元件對(Ci-1,Ci)之間都存在一個非同步的rolling merge過程,它負責在Ci-1超過閾值大小時,將記錄從較小的元件中移入到較大的元件中。當一個生命期很長的記錄被插入到LSM-tree之後,它首先會進入C0樹,然後通過這一系列的K個非同步rolling merge過程,最終將被移出到Ck。
這裡我們主要關注插入效能,因為我們假設LSM-tree通常使用在插入為主的場景中。對於三元件或者多元件LSM-tree來說,查詢操作效能上會有降低,通常一個磁碟元件將會帶來一次額外的頁面IO。
3.4 LSM-trees:Component Sizes
在本節中,我們將會推匯出具有多個元件的LSM-tree插入操作的IO開銷,同時會在數學上論證如何來為各個元件選擇最優的閾值大小。同時會對例3.3進行一些擴充套件,用來說明B-樹的系統開銷,採用兩元件LSM-tree所帶來的改進後的系統開銷,以及三元件LSM-tree帶來的更大的節省。
我們將LSM-tree元件的大小,S(Ci),定義為該元件葉子級別的記錄的位元組數;同時用Si來作為元件Ci的大小的縮寫,S(Ci)=Si,同時S代表所有元件的葉子級別的記錄的總大小,S=Sum(Si)。我們假設對於C0來說具有相對平穩的插入速率R,單位是bytes/秒,為了簡化起見,假設所有新插入的記錄都會存活下來並會通過一系列的rolling merge步驟被移出到Ck。同時假設元件C0,C1,C2…,Ck-1,都具有一個與當前分析所確定的最大閾值近似的大小。同時假設元件Ck具有一個相對穩定的大小,因為在一定的時間段內,刪除會將插入抵消掉。
給定一個具有固定總大小為S及C0元件大小為S0的K元件LSM-tree,該樹就可以通過變數ri(i=1,…k,代表了相鄰元件的大小之比,ri=Si/Si-1)來完整地描述了。如下所述,執行一個元件對(Ci-1,Ci)之間的正在進行中的merge操作的頁面IO頻率可以表示為R(向C0中的插入的頻率)和ri的函式。同時我們假設不同元件的blocks將會混合的方式來使用不同的磁碟以達到磁碟磁臂利用率的平衡,這樣最小化H就等價於最小化總的磁碟磁臂開銷(至少在磁碟磁臂開銷而非磁碟容量在成本中起決定性作用的情況下是這樣的)。這樣對於給定的R,找到可以使總的IO請求率H最小化的ri值的問題,就變成了一個標準了微積分最小化問題。在總大小S固定不變的假設下,由於ri值之間的複雜的遞迴關係將會使它變成一個很難解的問題。但是如果我們假設Sk是固定不變的(S0也是固定不變的),如Theorem3.1,當所有的ri值等於某個常數r時,就可以取得最小值。同時我們也通過Theorem3.2給出了在總大小S為常量時的,關於ri值的一個更為精確的解,同時也可以看到對於ri所取常量r值,基本上已經給出了非常類似的結果。假設所有的ri都取r值,那麼就有Si=(r^i)·S0。同時總大小可以表示為各元件的大小之和,S=S0+r·S0+(r^2)·S0+…+(r^k)·S0,因此我們就可以用S和S0來表示r。
根據Theorem3.1,對於給定的Sk,S0和插入頻率R,為了最小化多元件LSM-tree的總的IO頻率,我們需要讓最大元件Sk和最小元件S0之間的元件大小具有幾何級數的增長關係。另外我們將會發現,對於兩元件LSM-tree的情況,如果我們令R和Sk儲存不變,而允許S0可變,那麼H就可以表示為S0的函式,同時隨著S0的減小H會增大。現在我們可以通過改變S0的大小,來最小化LSM-tree的總開銷(記憶體+磁碟磁臂開銷)。例3.3演示瞭如何根據給定數目的元件來獲得最優的總開銷。現在總開銷中剩下的唯一的自變數就是元件數目,K+1了。在本節的最後我們會討論如何調節這個值。
Theorem3.1 給定一個最大元件大小為Sk,插入頻率為R和記憶體元件大小為S0的具有K+1個元件的LSM-tree,在比率ri=Si/Si-1等於同一個值r時,執行所有merge的總的頁面IO頻率H將可以取得最小值。這樣總大小S就可以表示為各元件大小之和:
(3.5) S=S0+r·S0+(r^2)·S0+…+(r^k)·S0
這樣我們就可以將r用S和S0表示出來。類似的,總的頁面IO頻率H可以表示為:
(3.6)H=(2R/Sp)·(K·(1+r)-1/2),Sp代表每個頁面的位元組數
證明:因為我們已經假設在記錄到達元件Ck之前都不會刪除,很明顯從元件Ci-1到Ci的rolling merge過程的記錄移出率應與插入到C0的頻率R(以bytes/秒為單位)是一致的,對於所有的i,0<i<=K。考慮Ci-1為磁碟元件的情況,從Ci-1到Ci的rolling merge過程將會包含從Ci-1中以每秒R/Sp個page的multi-page block讀取,此處Sp代表每個頁面的位元組數(通過假設所有碰到的記錄都會從Ci-1中刪除,我們根據R計算出了每秒從Ci-1中移出的記錄數目;在普通情況下,其他的假設也是可能的)。該merge過程還會包含從Ci中以每秒ri·(R/Sp)個page的multi-page block讀取(這是因為rolling merge遊標在掃描到的Ci中的page個數是Ci-1中的ri=Si/Si-1倍)。最後該merge過程還會引入每秒(ri+1)·(R/Sp)個page的multi-page磁碟寫入來將屬於Ci的merge出的新資料寫出。需要注意的是,在這裡我們考慮到了因為merge引起的Ci元件的增大{!資料從ri·(R/Sp)變成了(ri+1)·(R/Sp)個page}。將所有磁碟元件的Ci對應值相加,就可以得到總的multi-page IO大小H(以每秒的pages數為單位):
(3.7)H=(R/Sp)·((2·r1+2) + (2·r2+2) + … +(2·rk-1+2) + (2·rk+1))
每一項的(2·ri+k)代表了在元件Ci上的所有IO:ri·(R/Sp)用於為從Ci-1到Ci的merge讀入屬於Ci中的資料,(ri+1)·(R/Sp)用於為該merge將資料寫出到Ci,而R/S0則用於為從Ci到Ci+1的merge而讀入屬於Ci的資料。很明顯對於C0來說,沒有該項開銷,而對於Ck來說則沒有最後的那一項R/S0。因此公式(3.7)可以重新改寫為:
(3.8)H=(2R/Sp)( Σ(ri)+k-1/2),i為[1…k]
我們希望可以在如下條件下:Π(ri)=(Sk/S0)=C,, i為[1…k]{!根據之前的假設,我們知道S0和Sk均為常數,而Sk=(r1·r2…rk)·S0,故(r1·r2…rk)也是一個常數},可以最小化該函式值。為了解決這個問題,需要最小化Σ(ri),我們將rk使用C·Π(1/ri),i為[1…k-1]來替換掉{!根據Π(ri)=C,即可得rk= C·Π(1/ri)}。然後對每個自變數rj,j=1,…k-1計算其偏導數,同時令它們等於0,我們將會得到如下形式的一系列等式:0=1-(1/rj) C·Π(1/ri),i=1,…k-1,很明顯,當所有的rj(包括rk)等於C·Π(1/ri) 時,i=1,…k-1,或C^(1/k)時有解。
{!上面的問題本質上是如下的一個問題,K個正數的乘積為一定值,求這K個數的和的最小值,具體推導過程如下:
Σ(r1…rk)
= Σ(r1…rk-1)+ C·1/Π(r1…rk-1) //使用C·Π(1/ri)替換rk
= Σ(r1…rk-1)+ 1/rj·C·1/Π(r1…rj-1,rj+1…rk-1) //將rj提到前面
=1-(1/(rj·rj)) C·1/Π(r1…rj-1,rj+1…rk-1) //對rj進行求導
=1-(1/rj) C·Π(1/ri) //將其中一個rj放到後面
}
Theorem3.2 現在我們修改Theorem3.1中關於Sk大小為常數的假設,而是設總大小S為常數。這個最小化問題就變得更困難了,但是依然是可以通過拉格朗日乘數來解的。結果可以表示如下:
rk-1=rk+1
rk-2=rk-1+1/rk-1
rk-3=rk-2+1/(rk-1·rk-2)
我們忽略其證明過程。
正如我們通常所見到的,ri的值通常都是比較大的,比如是20或者更大,因此這個最大元件的大小,Sk通常佔據了總大小S的絕大部分。因此對於Theorem3.2來說,每個ri只是與它相鄰的那個ri+1有個細微的差異。因此,在下面的例子中,我們還是基於Theorem3.1的結果來進行說明。
最小化總開銷
通過Theorem3.1,可以看出如果讓R和Sk儲存不變,而允許S0變化,那麼就可以將總的IO頻率H表示為S0的函式。
根據公式(3.5) S=S0+r·S0+(r^2)·S0+…+(r^k)·S0,r會伴隨著S0的減小而增大,
又根據(3.6) H=(2R/Sp)·(K·(1+r)-1/2),H與r成正比
很明顯H會隨著S0的減小而增大。現在我們可以看下如何通過在昂貴記憶體與廉價磁碟間進行權衡,來讓兩元件LSM-tree的總開銷最小化。我們需要計算出用於儲存LSM-tree所需的磁碟空間,以及可以讓磁碟磁臂完全被利用所需要的總IO頻率H,同時我們將此作為我們用於決定可以最小化開銷的S0大小的計算的起點{!即先確定出一個H的初始值,而因為H與S0相互之間具有固定的關係,然後就可以根據這個H再計算出S0的一個初始值}。從這個點開始,如果我們繼續減少C0大小,那麼磁碟介質的開銷就需要增加了,因為此時磁碟磁臂開銷已經成為了限制因素。下面的例3.3就針對兩元件和三元件LSM-tree,解釋了這樣的一個過程。在講這個例子之前,我們首先針對兩元件情況進行一些分析。
總開銷是記憶體開銷,COSTm·S0,和磁碟開銷,磁碟開銷是由磁碟儲存和IO開銷中的最大值決定的,在這裡IO開銷基於multi-page block訪問頻率H(以pages/秒為單位):
COSTtot= COSTm·S0+max[COSTd·S1,COSTπ·H]
考慮兩元件的情況,對於公式(3.6),k=1,r=S1/S0。令:
s= (COSTm·S0)/(COSTd·S1)=記憶體開銷與S1資料的儲存開銷之比
t=2·((R/Sp)/S1)·(COSTπ/COSTd)·(COSTm/COSTd)
C=COSTtot/(COSTd·S1)=總開銷與S1資料的儲存開銷之比
那麼,利用式(3.6)進行替換和簡化後,同時假設S0/S1值很小,可以得出如下的近似等式:
C≈s+max(1,t/s)
{!推導過程如下:
C=COSTtot/(COSTd·S1)
= (COSTm·S0+max[COSTd·S1,COSTπ·H])/( COSTd·S1)
=(COSTm·S0) /( COSTd·S1)+max[1, COSTπ·H/( COSTd·S1)]
將H=2(R/Sp)(r+1/2)代入
=s+max[1,2((R/Sp)/S1)( COSTπ/COSTd)(r+1/2)]
=s+max[1,2((R/Sp)/S1)( COSTπ/COSTd)(COSTm/COSTd)(COSTd/COSTm)(r+1/2)]
= s+max[1,t(COSTd/COSTm)(r+1/2)
又r=S1/S0
≈s+max[1,t(COSTd/COSTm)(S1/S0)
≈s+max[1,t(COSTd·S1)/(COSTm·S0)]
≈s+max[1,t/s]
同時這也意味著COSTπ·H/( COSTd·S1)≈t/s
}
這樣相對開銷C就是變數t和s的函式;變數t實際上是應用程式所需的multi-page block IO頻度的某種形式化表示{!可以看到對於t來說,COSTπ/COSTd體現的是磁碟IO與空間開銷之比,COSTm/COSTd 體現的記憶體開銷與磁碟開銷之比,這兩個值是兩個常量,而((R/Sp)/S1)則體現的是資料的訪問密度,如果R越大,則t越大}。s則代表了為實現LSM-tree我們所需要提供的記憶體大小。為確定S0的大小,最簡單地規則就是,沿著s=t這個分界線,此時C=s+1,同時磁碟儲存和IO能力都已被完全利用{!s=t,根據COSTπ·H/( COSTd·S1)≈t/s說明此時COSTd·S1= COSTπ·H也就是說磁碟儲存空間所具有的IO能力已完全被利用 }。對於t<=1時,在s=t時可以取得最小值,但是對於t>1,C的最小值是沿著曲線s=t^(1/2)的,此時C=2·t^(1/2),將該結論代入到我們前面的等式中,可得到,對於t>=1:
(3.8)COSTmin=2[(COSTm·S1)(2·COST·R/Sp)]^(1/2)
{!COSTmin即min(COSTtot)}
因此在t>=1的情況下,LSM-tree的總開銷看起來是(足以將所有LSM資料儲存下來的所需記憶體的開銷,這是一個很高的值,即COSTm·S1)和(用於支援將插入資料寫入到磁碟所需的multi-page block IO的磁碟開銷,這是一個很低的值,即2·COST·R/Sp)的幾何平均數的2倍。總開銷中的一半將會被用於S0的記憶體開銷,剩下的一半用於對於S1的IO訪問開銷。磁碟儲存空間的開銷並沒有體現出來,因為t>=1保證了資料是足夠熱的足以讓IO開銷佔據磁碟開銷的主導。
在t<=1的情況下,資料較冷的情況下,在s=t時可取得最小化開銷,此時C=t+1<2。這意味著這種情況下的總開銷總是不會超過用於將S1儲存在磁碟上的開銷的兩倍。在這種情況下,我們會根據儲存開銷來決定磁碟大小,然後通過利用起它所有的IO能力來最小化記憶體使用。
{!我們可以將上面的分析過程轉換為一個更純粹的數學問題。t基本上可以看做一個常量,t與R是直接相關的,它標識了資料訪問的熱度,因此對於C的最小值計算實際上是在求如下曲線的最小值
C≈s+max(1,t/s)
C≈max(s+1,s+t/s)
由於t可看做一常量,這樣我們就可以將上面等式用平面座標系中的曲線進行表示。y=x+1和y=x+c/x,根據解析幾何知識可知,y=x+1是直線,而y=x+c/x則是如下形狀曲線。
當x->正無窮時,c/x->0,所以曲線無限接近y=x,而在x->0時,c/x->正無窮。最小值點可以通過對其求導數求得,在x=c^(1/2)時取得,此時y=2 c^(1/2)。下面需要求的則是y=max(x+1,x+c/x),因此需要將該曲線y= x+c/x與y=x+1進行比較。
x+1>x+c/x,可得x>c
x+1<x+c/x,可得x<c
也就是說以x=c作為分界點,y=max(x+1,x+c/x)取值如下:
在x>c時,y =x+1,而
在x<c時,y=x+c/x。
即y=max(x+1,x+c/x)就變成了一個分段函式,在x=c處分成了兩個函式。那麼下面的事情就是要看如何計算該分段函式的最小值了。
對於該分段函式來說,其在哪點取得最小值實際上就是與c的取值相關了,同時最小值點只有兩種可能:一是在y=x+1和y=x+c/x的交點處,一是在y=x+c/x的最小值點處。如果c=1,那麼y=x+1和y=x+c/x的交點就剛好是y=x+c/x的極值點,也就是說此時在x=c處即是最小值點,而在c<1時,二者交點在(c,c+1)處,但y=x+c/x的極值點在(c^(1/2),2 c^(1/2)),同時由於c<1故c^(1/2)>c,而我們的分段函式,在x>c處,曲線是y=x+1,因此此時的最小值應是在(c,c+1)處。只有當c>1時,c^(1/2)<c,此時分段函式曲線才是y=x+c/x,同時2c^(1/2)<c+1,因此此時的最小值點應是(c^(1/2),2 c^(1/2))。
}
例3.3 我們來考慮下例3.1中涉及的Account-ID||Timestamp索引。下面的分析只是計算出了在插入速率R為16,000bytes/秒(1000個16位元組的索引記錄,忽略其他開銷,這樣所產生的20天的索引資料將具有576,000,000個記錄,總大小約9.2GBytes)時,對應插入操作的開銷。
如我們在例3.1中所見,如果使用B-樹來進行索引支援,那麼磁碟IO會成為限制因素—葉級資料是warm的。即使假設所有的非葉子節點都是駐留在記憶體中的,仍然需要我們提供足夠的磁碟空間以支援H=2,000個隨機IO/秒,來滿足葉級節點隨機性的頁面更新的需求。如果採用3.1節裡的COSTp=$25,可以算出IO開銷是H·COSTp=$50,000。下面我們再計算下用於將上層節點快取在記憶體的開銷。假設葉節點是70%滿的,那麼每個葉節點就有0.7·(4K/16)=180個記錄,因此在葉級節點之上大概會有576,000,000/180=3,200,000個節點指向下面的葉子節點。如果我們採用一些字首壓縮,那麼我們就可以在該級別上的每個節點中放下200條記錄,這意味著將會有16,000{!3,200,000/200}個4Kbytes大小的頁面,總大小為64Mbytes,按照COSTm=$100/Mbytes算的話,就是$6400。我們忽略在該級別之上的那些節點的相對較小的快取開銷,那麼B-樹的總開銷就是由$50,000的磁碟開銷和$6400的記憶體開銷組成,合起來就是$56,400。
對於一個具有兩個元件C0和C1的LSM-tree來說,我們需要一個大小S1=9.2GB的磁碟來儲存記錄,開銷就是COSTd·S1=$9,200。我們會將資料在磁碟上緊密放置,同時計算出在這個容量大小下的磁碟在使用multi-page block IO的情況下所能支援的總IO速率H,實際上H=9200/COSTπ=3700pages/秒。現在根據這個H值,以及R=16,000bytes/秒,Sp=4K,再結合公式(3.6),就可以解出r。
{!(3.6)H=(2R/Sp)·(K·(1+r)-1/2),對於兩元件LSM-tree來說K=1,代入已知引數可得3700=(2·16,000/4000)·(r+1/2),解之得:r=3700/8-1/2=460}
根據r=S1/S0=460,以及S1=9.2GB,可得S0為20Mbytes,就需要$2000。這就是簡單地令s=t的情況下的解,總的開銷就是$11,200,同時磁碟的容量和IO已經被完全利用。因為此時t=0.22,故還屬於<1的情況因此這個解也就是最優解。我們可以再增加$200來為正在被merge的block購置2Mbytes的記憶體,那麼總的開銷就達到了$11,400。與B-樹的$56,400相比已經是非常大的改進了。
更詳細地說,插入速率R=16,000bytes/秒,意味著每秒需要有4個頁面(每個頁面4KB)從C0 merge到C1。因為C1大小大概是C0的460倍,那麼C0中的每條記錄merge時平均需要牽連到C1中的460條記錄。因此將C0中的一個頁面的merge需要讀寫C1中的460個頁面,每秒就是3680(C0每秒4個,4·460·2=3680)個頁面。這也是9.2個磁碟在使用multi-page block IO的情況下所能提供的IO能力,每個提供了400pages/秒{!根據前面所述,單磁碟隨機IO大概是40pages/秒,multi-page block IO大概是普通隨機IO的10倍}。
由於這個例子已經表明在具有兩個元件時,磁碟資源已經被完全利用,因此這裡也就沒有必要引入三元件LSM-tree。下面的例子展示了一個三元件LSM-tree可以為純插入負載提供更少的開銷的情況。
例3.4 考慮例3.3,令R增長10倍。現在B-樹解決方案為支援H=20,000IO/秒的IO頻率要花費$500,000來購買500GB的磁碟;其中有491GB的空間將會是空閒的。但是B-樹本身的大小是相同的,因此我們仍然需要為將目錄節點快取下來而支付$6400,這樣總開銷就是$506,400。對於LSM-tree來說,如果R增長了10倍,意味著t也增長了同樣的倍數,即從0.22增長到了2.2。因此現在t就是大於1的了,那麼此時最優的兩元件解決方案已無法利用起所有的磁碟能力{?為何?}。我們利用公式(3.8)來計算出兩元件LSM-tree的最小開銷為$27,000,其中一半用於13.5GB的磁碟,一半用於135MB的記憶體。此時,仍有4.3GB的磁碟空間未被使用,加上用於快取的2MB記憶體,總開銷是$27,200。
更詳細地來說,插入頻率R=160,000bytes/秒,意味著每秒需要有40個頁面(每個頁面4KB)從C0 merge到C1。因為C1大小大概是C0的68倍,因此將C0中的一個頁面的merge需要讀寫C1中的68個頁面,每秒就是5440(C0每秒40個,40·68·2=5440)個頁面。這也是13.5個磁碟在使用multi-page block IO的情況下所能提供的IO能力。
對於使用一個三元件LSM-tree來處理這種R=160,000bytes/秒的情況,可以按照兩元件的那種方式先計算出最大的那個磁碟元件的一個開銷以及IO頻率的一個開銷平衡點。根據理論3.1,我們有Si/Si-1=r,i=1,2,我們可以計算出r=23並且S0=17MB(記憶體開銷就是$1700)。
{!與例3.3類似的計算方法,根據(3.6)H=(2R/Sp)·(K·(1+r)-1/2),對於三元件LSM-tree來說K=2,代入已知引數可得:
3700+3700/r=(2·160,000/4000)·(2·(1+r)-1/2),
解這個二元一次方程得:r=23,S0=S2/(23·23)=17MB,為存放下所有資料S2=9.2GB}
現在從這個點開始繼續增加記憶體的大小不會得到更好的價效比,因為記憶體大小的降低,將會導致相應的磁碟開銷的平方級別的增長。因此我們可以得到一個針對三元件的類似的s=t解決方案。在加一個4MB記憶體用於為兩個rolling merge過程快取資料,需要$400,這樣對於三元件LSM-tree的總開銷就是$9200的磁碟開銷,加上$2100的記憶體開銷,總共就是$11,300,又比兩元件LSM-tree的開銷降低了很多。
更詳細地來說,記憶體元件C0大小為17MB,稍小的那個磁碟元件大小C1是它的23倍,大概是400MB,C2又比C1大23倍,是9.2GB。那麼每個頁面在從C0 merge到C1時,將會引入23個頁面讀和23個頁面寫操作,這樣每秒就需要讀寫1840(40*23*2)個頁面。類似的,每秒也需要有40個頁面從C1 merge到C2,每個頁面也會引入23個頁面讀和23個頁面寫操作,這樣每秒也會需要讀寫1840(40*23*2)個頁面{!?有些疑問,按照之前的說法,rolling merge過程,元件越大需要的IO量應該是越多,為何此處變成一樣了呢?}。這樣總的IO頻率就是3680,剛好是9.2G的磁碟在使用multi-page block IO的情況下所能提供的IO能力。
與普通B-樹相比,對於查詢操作來說,兩元件或三元件LSM-tree需要更多的IO。對於它們來說,最大的那個元件就非常像一個獨立的B-樹結構,但是對於LSM-tree的情況,我們還沒有付出為快取那些索引中的葉級以上的節點而產生的$6400的記憶體開銷。因為在樹中的那些很高層的節點相對很少,基本可以忽略,同時我們還可以假設它們已經被快取了。但是,如果查詢頻率足夠高我們也很樂意花錢來將所有的目錄節點快取起來。在三元件的情況下,我們還需要考慮C1元件。因為它比最大的那個元件小23倍,因此我們也可以很容易地快取它的所有非葉子節點,同時我們把這個開銷計入到我們的分析中。在某個C2中的記錄被查詢時,如果訪問到C1中的一個未被快取的節點將會引入一次額外的讀,同時也需要決定是否快取C2的目錄節點。因此,對於三元件情況來說,查詢操作將可能會需要讀取一些額外的頁面而超過簡單B-樹結構所需的2次IO。對於兩元件的情況,可能需要一次額外的讀。如果我們為快取LSM-tree元件的那些非葉子節點購置了足夠的記憶體,那麼對於兩元件的情況我們就能達到B-樹的訪問速度,對於三元件的情況可能只在某些時候需要一次額外的讀。加上快取的開銷後,三元件LSM-tree的開銷就變成了$17,700,仍然遠小於B-樹。但是可能可以以更好的方式來用這些錢:一個充分的分析需要能夠最小化在所有工作負載(包括更新和查詢)上的總開銷。
我們已經根據定理3.1通過改變ri的大小來達到了使得merge操作的所需的IO的最小化。然後又通過選擇S0來使得總開銷最小化,以得到最好的磁碟磁臂和記憶體開銷。現在對於LSM-tree來說,唯一可變的就是元件個數,K+1了。實際證明,當我們增加元件個數的時候,S0將會不斷減少直到到達某個固定點,此時元件間的大小比率將會達到一個值e=2.71…….,或者直到到達cold-data區域。但是,通過例3.4可以看出,隨著元件數的增加,S0對總開銷的影響將會越來越小;在三元件LSM-tree的情況下,S0的大小已經降低到17MB。此外,隨著元件數目的增長還會伴隨著其他的開銷:需要有更多的CPU來執行更多的rolling merge過程,需要有更多的記憶體來快取這些merge過程中的節點(甚至可能會查過C0的記憶體開銷)。此外查詢操作將需要在所有的元件樹中進行查詢。如上這些原因就對元件的數目加上了很多限制,通常實際中最常見的的就是三元件的情況。
相關文章
- The Log-Structured Merge-Tree(譯):上Struct
- The Log-Structured Merge-Tree(譯):下Struct
- 【譯】JavaScript中的PromisesJavaScriptPromise
- 【譯】JavaScript中的CallbacksJavaScript
- life is short 中譯本(嘗試中)
- [Web翻譯]JavaScript中的編譯與填充WebJavaScript編譯
- 反編譯系列教程(中)編譯
- [譯] React 中的排程React
- [譯] 理解 JavaScript 中的 undefinedJavaScriptUndefined
- [譯] Javascript 中多樣的 thisJavaScript
- [譯]Perl中的陣列陣列
- LSM 優化系列(三)-- SILK- Preventing Latency Spikes in Log-Structured Merge Key-Value Stores ATC‘19優化Struct
- life is short 中譯本(粗校)
- [Flutter翻譯]Flutter中的剪下Flutter
- 【譯】Flutter中的花式背景動畫Flutter動畫
- JavaScript中的Object相等(譯文)JavaScriptObject
- [譯]理解js中的event loopJSOOP
- [譯]Swift 中的型別擦除Swift型別
- [譯] JavaScript 中的私有變數JavaScript變數
- (譯)理解javascript中的作用域JavaScript
- 【譯】JavaScript中的async/awaitJavaScriptAI
- [譯] Swift 模組中的 API 汙染SwiftAPI
- 【譯】Flutter 2.2中的新功能Flutter
- [譯]理解JS中的閉包JS
- [譯] JavaScript中的“this”是什麼?JavaScript
- [譯] Swift 中的動態特性Swift
- [譯] TensorFlow 中的 RNN 串流RNN
- [譯]javascript中的依賴注入JavaScript依賴注入
- 【譯】理解Rust中的Futures (一)Rust
- 【譯】理解Rust中的閉包Rust
- Linux中gcc編譯工具LinuxGC編譯
- 【譯】理解Rust中的Futures(二)Rust
- [譯] $digest 在 Angular 中重生Angular
- [譯] 探究 Swift 中的 Futures & PromisesSwiftPromise
- 【譯】Python中如何建立mock?PythonMock
- The Sky Crawlers(中譯:空中殺手)
- 在 Windows 中編譯 Github 中的 GO 專案Windows編譯GithubGo
- FreeBSD中的GNU C編譯器--編譯器GCC(轉)編譯GC