Bw樹:新硬件平台的B樹(內存數據庫中的b樹索引)


Bw樹:新硬件平台的B

Bw樹:新硬件平台的B... 1

1. 概述... 2

1.1 原子記錄存儲(Atomic Record Stores... 2

1.2 新的環境... 2

1.3 實現... 3

2 Bwtree的體系結構... 3

2.1 現代的硬件敏感性... 3

2.2 Mapping Table. 4

2.3 增量更新... 4

2.4 bwtree結構修改... 4

2.5 日志結構化存儲(LSS). 4

2.6 管理事務日志... 4

3 內存中Latch Free Page. 4

3.1 靈活的虛擬頁... 5

3.1.1 更新... 5

3.1.2 葉子級別的更新... 5

3.1.3 page查詢... 5

3.2 頁固化... 6

3.3 區間掃描(Range Scans). 6

3.4 回收... 6

4 bwtree的結構修改... 6

4.1 節點分裂(Node Split). 6

4.1.1 子節點分裂(Child Split). 6

4.1.2 更新父節點... 6

4.1.3 固化... 7

4.2 節點合並(Node Merge). 7

4.3串行結構修改和更新... 8

5 緩存管理... 8

5.1 提前寫日志協議LSN.. 8

5.2 Flush頁到LSS. 9

5.2.1 page 排列... 9

5.2.2 增量flush. 9

5.2.3 Flush活動... 9

6 性能評估... 9

6.1 實現和安裝... 9

6.2 Bw-tree調整和屬性... 9

6.3 bw-tree和傳統btree對比... 11

6.4 BwtreeSkip list比較... 11

6.5 Cache的效率... 12

參考:... 12

 

1. 概述

1.1 原子記錄存儲(Atomic Record Stores

很多現在討論的NO-SQL本質上是原子記錄存儲,很多都是獨立的產品,但是也可以使完整事務系統的一個組件。

ARS支持每個獨立的記錄的讀寫,記錄都是以key來識別。基於樹的ARS可以提供更快的key range 掃描。ARS不單單是訪問方法,也包含了固態存儲的管理,並且要求在系統奔潰之后可以恢復。Bw樹就是利用ARS形成新的b樹。

1.2 新的環境

處理器已經被修改,不再提高單個內核性能。已經有了以下一些修改:
1.
針對多核的設計:現在大多數的處理器都是高性能多核處理器。單核速度提升變慢,因此為了更好的內核,需要注意以下兩點:
                a.
多核cpu提高了高並發,並發的增加會導致latchblock,限制了可擴展性。
                b.
好的多核處理器依賴於高cpu cachehit率。
對於第一點,bwtreelatch-free,這樣thread在碰到沖突的時候,不會被yield或者重定向。對於第二點,bwtree使用增量更新,避免page中的更新,保護之前cache line的地址。
2.
針對現代存儲的設計:磁盤的延遲是主要的問題,flash存儲有很快的隨機和順序讀取性能,但是因為在寫入之前需要先擦除,所以隨機寫會比順序寫速度要慢。Bwtree會生產日志結構,這種方法可以避免FTL保證寫入性能盡量的高。

1.3 實現

1.Bwtree通過mapping table來組織,page的大小和位置被虛擬化。實際上是對latch-free和日志結構的虛擬化。
2.
通過在原來的page前加上一個增量記錄來更新bwtree
3.
pagesplittingmerge做了設計。SMOs由多個原子操作實現,若thread發現有在處理的SMO操作,並不會堵塞而是來完成SMO操作。
4.
日志結構存儲(LSS),是名義上的page存儲,實際上是通過post增量的修改來提高存儲的效率。
5.
根據LSSbwtree來實現ARS
會做這些實現是因為,認為latch free技術和狀態修改避免了update-in-place可以再當前處理器上得到性能提升。

2 Bwtree的體系結構

Bw-tree是典型的b+樹,提供對數級的訪問,如圖:

Bw-tree
層在最上面,和Cache層交互,cache管理建立在存儲層之上,實現了LSS。現在LSS是使用flash存儲,但也可以支持磁盤。

2.1 現代的硬件敏感性

bwtree中,threads基本不會block,消除latch是設計的目的之一。我們使用原子比較切換指令(CAS)來代替latchBwtree只會在從固態存儲中獲取page是才會block(也就是LSS)。持續的threads運行保障了內核指令cache,避免線程的空閑時間和上下文切換的開銷。甚至使用增量來更新,而不是update-in-place,從來避免cpu cachemiss,提高cpu cachehit率。
數據管理系統的瓶頸往往是在IO上,所以我們選用了flash存儲,使用SSD掛載,但是還是會限制性能。LSS存儲啟用了大的寫入buffer來消除寫入瓶頸,flash存儲的高隨機讀取能力和大緩存組合何以最小化讀取的block。大的多頁buffer允許我們寫入改變page的大小,不需要填充到一個統一的偏移大小。

2.2 Mapping Table

Mapping TableCache層維護,mapping table包含了物理頁到邏輯頁的映射,每個邏輯頁都有一個PID來識別。PID可以通過mapping table翻譯成內存的物理地址或者flash的偏移地址。Bwtree就是通過PID來構建一個b+樹。
mapping table
隔離了物理地址和bwtree節點,這樣每次修改page或者寫入到固態存儲不需要把位子的修改傳播到樹的根部,即更新節點內部的連接。這樣的重定向可以支持在內存中的增量修改可以支持在固態存儲的LSS
因為bwtree是邏輯的不是固定的物理地址,就可以根據自己需要制定nodepage

2.3 增量更新

Page的狀態改變是通過在原來的page前加一個增量記錄來實現的。使用CAS指令把增量記錄的物理內存地址放到mapping tablepage物理地址欄中。如果成功增量記錄地址會變成的page 的新物理地址。這個策略被使用在數據修改,也被使用在樹的結構修改。
間歇的來固化page(創建一個新的page把所有的增量修改合並),減少鏈的長度,提高查詢的性能。固化的方式也是通過CAS指令實現,之前的page也會被回收。
在增量更新的同時,可以支持同時在bwtree中做latch-free的訪問並且能夠防止update-in-place保護cpu cacheMapping tablebwtree重要的特性,可以隔離直接對node更新。

2.4 bwtree結構修改

Latch並不會對做結構修改的page進行保護,如page split
為了解決這個問題,就把SMO放在一個順序的原子化操作中,每個操作都是通過CAS進行。為了保證沒有線程等待SMO。如果一個線程看到一個未完成的SMO,會在執行自己的線程前先去完成它。

2.5 日志結構化存儲(LSS)

Page批量的順序寫入,大大減少了IO次數。因為回收機制,日志結構通常會有額外的寫入來重定位page,用來回收日志的存儲空間。
當刷新page的時候,只需要刷新增量部分,增量的部分表示從上次刷新到當前的增量修改。通過增加flush bufferpage數量來減少IO的消耗。但是這樣會有讀懲罰,因為page中的數據不是連續被存放的。
LSS
會清理之前的部分flash,這部分flash表示之前的數據。通過清理,LSSpage和它的增量連續的存放,可以提高訪問性能。

2.6 管理事務日志

和傳統的數據庫系統一樣,ARS也要在系統crash時保持數據一致性。通過log sequence number(LSN)來表示每個更新操作。
和傳統系統一樣,page的刷新的懶惰的,並且依賴於 write-ahead log協議(WAL)。不同的是在WAL之前不會堵塞pageflush。因為現在的update生成的基於之前page的增量。

3 內存中Latch Free Page

這里主要介紹內存中的bwtree page,主要討論:
1.
基本的page結構和如何以latch free 方式更新page
2.page
固化是的查詢更加高效。
3.
使用epoch的內存回收機制。

3.1 靈活的虛擬頁

Bwtree存儲的信息和b樹類似,索引節點包含了以key排序的keypointer數據對。數據節點包含keyrecord對。另外還保存了page中保存的最小的key和最大的key,和一個可以指向右邊兄弟節點的指針。
bwtree
page是邏輯的,不是固定物理地址,大小也不是固定的,有了兩個重要的特性讓bwtree比較特殊:
1.
使用PID來識別一個page,使用pid轉化為物理地址來訪問page
2.page
的大小是靈活的,沒有限制page的大小。Page的增長通過增量記錄來實現。

3.1.1 更新

更新不會是in-place更新,而是通過創建一個增量記錄來描述更新,並放置在當前page之前:
1.
創建一個增量記錄,指向當前page
2.
獲取pagemapping table上的項。
3.
增量記錄的物理地址作為page新物理地址,使用CAS指令來實現install

如圖,多更新幾次之后會形成增量記錄鏈。

3.1.2 葉子級別的更新

在葉子級別,增量分為3種:isnertupdatedelete。所有的增量都包含LSN。使用LSN來做還原機制涉及到日志的管理方式必須是WALInsertupdate增量包含了新的記錄,但是delete只要含了要刪除的key

3.1.3 page查詢

葉子級別的page查詢涉及到增量鏈的遍歷。查詢會在第一個查詢到的地方停止,若key出現在updateinsert就返回,如果是delete那么就查詢失敗。如果增量鏈不包含key,查詢執行在base page 上的binary查詢。

3.2 頁固化

隨着增量鏈變成,查詢性能會降低,為了避免這種情況,會間歇的執行頁固化。會根據增量記錄和base page重組生成新的page。頁固化會在增量鏈長度超過閥值是觸發。
當固化是線程會做以下操作:
1.
創建一個新的page,然后把最新的記錄版本寫入到page
2.
使用CAS指令install新的page。若成功請求回收老的page,若失敗釋放新的page。失敗並不會重試,后面的線程會繼續執行直到完成。

3.3 區間掃描(Range Scans)

區間掃描是對一個指定key區間進行掃描。掃描可以指定順序還是倒序來獲取記錄。
掃描維護了一個游標標記掃描的進度。對於新的掃描記錄的是lowkey。當掃描碰到第一個在區間內的記錄會構建一個向量,並放入向量內。向量用來保存掃描的結果。獲取下一行有原子性,但是整個掃描沒有。
事務鎖會阻止能夠看到的記錄,但是我們並不知道沒有傳輸的記錄。所以在傳輸之前我們會檢查是否有修改。如果有修改,我們會重新構建一個記錄向量。

3.4 回收

Latch-free環境不允許排它的訪問一個共享數據結構,也就是說即使page在被修改,也可以被訪問。我們並不想釋放一個任然被訪問的內存。比如在固化的時候,線程交換老的page和新的page狀態,並回收新狀態,但是不能再有線程訪問老的page的時候釋放。
epoch
機制就是為了保護之前有訪問的又要釋放的對象。

4 bwtree的結構修改

4.1 節點分裂(Node Split)

分裂是由一個后台線程在page的大小超過系統設置的閥值的時候就進行分裂。整個過程分為2個階段:
1.
現在葉子上做split.
2.
然后更新父的節點.
如果有必要可以一直向上遞歸。因為有了blink指向兄弟節點,就可以把split分為2個獨立的操作。

4.1.1 子節點分裂(Child Split)

為了分裂節點P,大概分為2步:
1.btree
層先分配一個節點Q,然后找到合適的key的位置Kp,把大於Kpkey復制到Q,然后Qside link指向R
2.
P之前加入一個Split delta記錄,記錄包含了2個信息,a.Pkey大於Kp的都不可用。Side link指向Q。這個時候索引還是可用的,盡管父節點O中沒有指向Q。所有包含在Qkey都會被指引到P上。到了Split Delta發現key大於Kp那么就會從 side link被指引到Q上。

4.1.2 更新父節點

為了能夠直接從父節點O指向到Q,需要在O上加一個delta record,這個delta record包含3個信息:
1.Kp,P
Q的分隔key
2.
一個指向Q的指針
3.Kq,Q
的分隔key,原來是P的分隔key
delta record出現邊界key KpKq是一種優化可以提高查詢速度,因為查詢必須遍歷index節點上的delta chain,若發現要查詢的keyKpKq之間就可以馬上指向Q沒必要在去遍歷整個index節點了。

4.1.3 固化

相對於增加delta來說,使用新的base pagesplit的時候延遲會加大。減少延遲也減少了split的錯誤。

4.2 節點合並(Node Merge)

和分裂類似,節點合並是當node低於一個值的時候就會發生。

1.刪除節點,如要合並R,先在R上標記刪除,添加一個delta record,表示R已經被刪除。如果一個線程要讀取R中的數據碰到Remove Node Delta Record,就會應用在左邊的兄弟上。
2.
合並孩子節點,在L上添加一個node merge delta物理的指向R(使用內存地址),這樣就表示R中的數據是在節點L中。R的存儲狀態被傳換成了L,只有當L固化的時候page R才會被回收。當對L查詢時,變成對樹的查詢,可以再L中訪問L中和R中的key。為了讓這個可用正常,merge delta上會有key的分隔,用於訪問正確的節點。
3.
更新父節點,通過使用delete delta刪除父節點上關於R的索引信息和LKey信息。
一旦在父節點加上了delta,所有到R的路徑就全部被堵塞了。就可以啟動機制來回收R了,因為epoch的保護機制,所有直到所有的線程訪問完之后才會被回收。

4.3串行結構修改和更新

為了正確的序列化SMOs和數據修改,SMOsSMOs,我們需要構建一個在bwtree上的序列化調度。我們想要把SMO當成一個原子化操作,並且沒有Latch。為了滿足這個需求,所以線程必須在更新數據,或者執行自己的SMO之前完成之前的SMO操作。

5 緩存管理

Cache層是負責對讀取,刷新和內存與flash之間page交換。維護mapping table提供抽象的邏輯頁給bwtree。在mapping table上要不就是內存地址,要不就是flash的偏移。如是flash偏移那么就從LSS上讀入到內存中。所有涉及到內存的操作都是通過CAS來完成。
有很多原因會導致內存被刷入到flash中。如,因為bwtree是事務系統的一部分所以flush update可以在事務日志上checkpointPage flush也處理page 換出吧flash偏移installmapping table上,並且回收內存減少內存的使用。
為了跟蹤固態存儲中page 的版本和位置,我們使用flush delta record,還記錄了那些修改被flush了,之后的flush只對應增量即可,若flush page成功,flush delta就會包含新的flash offsetpage的狀態會被設置為flushed

5.1 提前寫日志協議和LSN

BwtreeARS(原子記錄存儲),可以被包含在事務系統里,若被包含就在事務方面被強化。LSN:記錄的插入和修改都會使用LSN來標記,而被flush的最大LSN被記錄在flush delta上。事務日志協作:不管什么時候TC(事務控制器)flush到固態存儲上,左右一個LSN被稱為ESL。所有小於ESLLSN都會被刷新到固態存儲中。然后TC定期的把ESL發送到DC,根據提前寫日志協議,DC不能刷新大於ESL的數據。
DC
中的page flush是由TC根據redo-scan-start-point(RSSP)來要求的,這樣RSSP之前的日志都可以被截斷。TC會等待來自DC的通知,表示LSN<RSSP的數據都已經被固化了,因為這樣數據被固化,在redo 的時候就沒必要再去redo這些日志了。

5.2 Flush頁到LSS

LSS提供了一個很到的buffer,里面存放了bwtree的結構修改和數據修改。

5.2.1 page 排列

Cache managerpage中的byte以線性方式寫入flush bufferPage的狀態在試圖刷新的時候被獲取。這個很重要,因為之后的修改可能會和split或者固化沖突。

5.2.2 增量flush

flushpage,緩沖管理器只排列那些ESL>LSN的增量的記錄。增量flush表示刷新的消耗要比刷新整個page 小。日志結構存儲有2個好處:
1.flush buffer
可以包含更多的update
2.
回收不需要很頻繁因為消耗不是很大。

5.2.3 Flush活動

Flush buffer聚合了LSS到達一個閥值(1MB)然后寫入,這樣減少了IO的開銷。並且使用雙buffer,這樣當前的buffer還在處理時,可以准備下一個進程了。
flush buffer IO
完成之后,相關page 的狀態就會被修改。Mapping table獲取flush的結果然后使用flush delta來描述。
緩沖管理監控bwtree的內存使用,超過配置的閥值,會視圖把page切換到lss上,一旦page被清空,就會從緩存中被犧牲。被犧牲的page 通過epoch被回收。

6 性能評估

主要介紹 bwtree和其他存儲結構的性能對比

6.1 實現和安裝

BWtreebwtree以獨立的記錄原子存儲實現,借用了win32原生的CAS interlockedCompareExchange64
BerkeleyDB
BerkeleyDBk-v數據庫,我們使用c語言實現了btree模式。
Skip list
:跳表可以實現latch-free,可以實現快速插入以及對數級的查詢。
測試環境:Intel Xeon W3550,24GB內存
測試數據庫:XboxLiveStorage deduplication traceSynthetic
默認:主要性能的單位是million/sec。為每個工作負荷提供8個工作線程和邏輯內核數一樣。

6.2 Bw-tree調整和屬性

這節主要介紹2方面對bwtree的性能影響:
1.delta chain
的長度對性能影響

如圖,bwtree運行在SyntheticXbox不同的delta chain對性能的影響。即consolidation閥值。對於小的閥值,consolidation會頻繁出現,影響性能。對於xbox,查詢性能在閥值大於4之后開始惡化。雖然順序掃描,分支預測性能良好,因為xbox100byte,使得L1的填充率下降。synthetic只有8byte大小,因此delta chain可以更長,性能也不受影響。
2.latch free
使用CASCAS的錯誤率bwtree原生latch-free,在某些情況下會出現更新page狀態的錯誤。

更新錯誤率很低大概占用0.02%的工作負荷。splitconsolidationupdate錯誤率大得多,這個也是在預料中的,因為他們會和update是競爭關系。synthetic是一個極端場景,因為update性能很好,導致splitconsolidation的錯誤率很高。

6.3 bw-tree和傳統btree對比

主要導致性呢過問題的有2個原因:
1.latch-free
bwtree沒有latch,但是berkelydb確有性能問題。
2.cpu
cache效率,bwtree使用delta record來進行更新,base page不變,cpu cache很少出現不可用的情況。但是BerkeleyDBin-place update,會導致更多的cpu cache不可用的情況。

6.4 BwtreeSkip list比較

Skip list可以提供有序的對數查詢,並且很容易實現latch free

如圖比較了在bwtreeskiplist下的性能,bwtreeskiplist性能要好。是因為bwtreecpu cache的效率比skiplist 要搞。

6.5 Cache的效率

為了更深入的測量和比較bwtreeskiplist,我們使用intel VTune來獲取cpu cachehit

通過如圖,會發現bw-treecache hit率比skiplist要高,這個也就是為什么bwtree性能比skiplist好的原因。

參考:

The Bw-Tree: A B-tree for New Hardware

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM