本系列文章將從邏輯正確、內容完整的角度全面介紹WiredTiger存儲引擎。
本篇作爲WiredTiger存儲引擎介紹系列文章第二篇,包含如下內容:
-
Page的生命週期
-
Page的狀態
-
Page相關參數與調優
通過第一篇介紹,我們瞭解到數據以page爲單位加載到cache、cache裏面又會生成各種不同類型的page及爲不同類型的page分配不同大小的內存、eviction觸發機制和reconcile動作都發生在page上、page大小持續增加時會被分割成多個小page,所有這些操作都是圍繞一個page來完成的。
因此,有必要系統的分析一頁page的生命週期、狀態以及相關參數的配置,這對後續MongoDB的性能調優和故障問題的定位和解決有幫助。
Page的生命週期
Page的典型生命週期如下圖所示:
圖:page的生命週期
第一步:pages從磁盤讀到內存;
第二步:pages在內存中被修改;
第三步:被修改的髒pages在內存被reconcile,完成後將discard這些pages。
第四步:pages被選中,加入淘汰隊列,等待被evict線程淘汰出內存;
第五步:evict線程會將“乾淨“的pages直接從內存丟棄(因爲相對於磁盤page來說沒做任何修改),將經過reconcile處理後的磁盤映像寫到磁盤再丟棄“髒的”pages。
pages的狀態是在不斷變化的,因此,對於讀操作來說,它首先會檢查pages的狀態是否爲WT_REF_MEM,然後設置一個hazard指針指向要讀的pages,如果刷新後,pages的狀態仍爲WT_REF_MEM,讀操作才能繼續處理。
與此同時,evict線程想要淘汰pages時,它會先鎖住pages,即將pages的狀態設爲WT_REF_LOCKED,然後檢查pages上是否有讀操作設置的hazard指針,如有,說明還有線程正在讀這個page則停止evict,重新將page的狀態設置爲WT_REF_MEM;如果沒有,則pages被淘汰出去。
Page的各種狀態
針對一頁page的每一種狀態,詳細描述如下:
l WT_REF_DISK:初始狀態,page在磁盤上的狀態,必須被讀到內存後才能使用,當page被evict後,狀態也會被設置爲這個。
l WT_REF_DELETED:page在磁盤上,但是已經從內存B-Tree上刪除,當我們不在需要讀某個leaf page時,可以將其刪除。
l WT_REF_LIMBO:page的映像已經被加載到內存,但page上還有額外的修改數據在lookasidetable上沒有被加載到內存。
l WT_REF_LOOKASIDE:page在磁盤上,但是在lookasidetable也有與此page相關的修改內容,在page可讀之前,也需要加載這部分內容。
當對一個page進行reconcile時,如果系統中還有之前的讀操作正在訪問此page上修改的數據,則會將這些數據保存到lookasidetable;當page再被讀時,可以利用lookasidetable中的數據重新構建內存page。
l WT_REF_LOCKED:當page被evict時,會將page鎖住,其它線程不可訪問。
l WT_REF_MEM:page已經從磁盤讀到內存,並且能正常訪問。
l WT_REF_READING:page正在被某個線程從磁盤讀到內存,其它的讀線程等待它被讀完,不需要重複去讀。
l WT_REF_SPLIT:當page變得過大時,會被split,狀態設爲WT_REF_SPLIT,原來指向的page不再被使用。
Page的大小參數
無能將數據從磁盤讀到內存,還是從內存寫到磁盤,都是以page爲單位調度的,但是在磁盤上一個page到底多大?是否是最小分割單元?以及內存裏面的各種page的大小對存儲引擎的性能是否有影響?本節將圍繞這些問題,分析與page大小相關的參數是如何影響存儲引擎性能的。
總的來說,涉及到的關鍵參數和默認值如下表所示:
表:關鍵page參數和默認大小
參數名稱 |
默認配置值 |
含義 |
allocation_size |
4KB |
磁盤上最小分配單元 |
memory_page_max |
5MB |
內存中允許的最大page值 |
internal_page_max |
4KB |
磁盤上允許的最大internal page值 |
leaf_page_max |
32KB |
磁盤上允許的最大leaf page值 |
internal_key_max |
1/10*internal_page |
internal page上允許的最大key值 |
leaf_key_max |
1/10*leaf_page |
leaf page上允許的最大key值 |
leaf_key_value |
1/2*leaf_page |
leaf page上允許的最大value值 |
split_pct |
75% |
reconciled的page的分割百分比 |
詳細說明如下:
l allocation_size:
MongoDB磁盤文件的最小分配單元(由WiredTiger自帶的塊管理模塊來分配),一個page的可以由一個或多個這樣的單元組成;默認值是4KB,與主機操作系統虛擬內存頁的大小相當,大多數場景下不需要修改這個值。
l memory_page_max:
WiredTigerCache裏面一個內存page隨着不斷插入修改等操作,允許增長達到的最大值,默認值爲5MB。當一個內存page達到這個最大值時,將會被split成較小的內存pages且通過reconcile將這些pages寫到磁盤pages,一旦完成寫到磁盤,這些內存pages將從內存移除。
需要注意的是:split和reconcile這兩個動作都需要獲得page的排它鎖,導致應用程序在此page上的其它寫操作會等待,因此設置一個合理的最大值,對系統的性能也很關鍵。如果值太大,雖然spilt和reconcile發生的機率減少,但一旦發生這樣的動作,持有排它鎖的時間會較長,導致應用程序的插入或修改操作延遲增大;如果值太小,雖然單次持有排它鎖的時間會較短,但是會導致spilt和reconcile發生的機率增加。
l internal_page_max:
磁盤上internalpage的最大值,默認爲4KB。隨着reconcile進行,internalpage超過這個值時,會被split成多個pages。
這個值的大小會影響磁盤上B-Tree的深度和internalpage上key的數量,如果太大,則internalpage上的key的數量會很多,通過遍歷定位到正確leaf page的時間會增加;如果太小,則B-Tree的深度會增加,也會影響定位到正確leaf page的時間。
l leaf_page_max:
磁盤上leaf page的最大值,默認爲32KB。隨着reconcile進行,leaf page超過這個值時,會被split成多個pages。
這個值的大小會影響磁盤的I/O性能,因爲我們在從磁盤讀取數據時,總是期望一次I/O能多讀取一點數據,所以希望把這個參數調大;但是太大,又會造成讀寫放大,因爲讀出來的很多數據可能後續都用不上。
l internal_key_max:
internalpage上允許的最大key值,默認大小爲internalpage初始值的1/10,如果超過這個值,將會額外存儲。導致讀取key時需要額外的磁盤I/O。
l leaf_key_max:
leaf page上允許的最大key值,默認大小爲leaf page初始值的1/10,如果超過這個值,將會額外存儲。導致讀取key時需要額外的磁盤I/O。
l leaf_value_max:
leaf page上允許的最大value值(保存真正的集合數據),默認大小爲leaf page初始值的1/2,如果超過這個值,將會額外存儲。導致讀取value時需要額外的磁盤I/O。
l split_pct:
內存裏面將要被reconciled的 page大小與internal_page_max或leaf_page_max值的百分比,默認值爲75%,如果內存裏面被reconciled的page能夠裝進一個單獨的磁盤page上,則不會發生spilt,否則按照該百分比值*最大允許的page值分割新page的大小。
專欄作者:郭遠威
MongoDB中文社區委員,長沙分會主席
《大數據存儲MongoDB實戰指南》作者
資深大數據架構師,通信行業業務架構與數據遷移專家