MySQL記憶體管理

caohongfeng666發表於2021-01-03

標籤:MySQL

標籤:MySQL結構

標籤:MySQL記憶體管理


1. Buffer Pool主要的基礎結構buf_pool_t、buf_block_t和buf_page_t。

(1). 

Buffer Pool例項,大小等於innodb_buffer_pool_size/innodb_buffer_pool_instances,

每個Buffer Pool Instance都有自己的鎖,訊號量,物理塊(Bufferchunks)以及邏輯連結串列(List)。

即各個instance之間沒有競爭關係,可以併發讀取與寫入。所有instance的物理塊(Buffer chunks)在資料庫啟動的時候被分配,直到資料庫關閉記憶體才予以釋放。

每個Buffer Pool Instance有一個page hash連結串列,透過它,使用space_id和page_no就能快速找到已經被讀入記憶體的資料頁,而不用線性遍歷LRU List去查詢。

page hash是為了避免掃描LRU List。


struct buf_pool_t { //儲存Buffer Pool Instance級別的資訊

    ...

    ulint instance_no; //當前buf_pool所屬instance的編號

    ulint curr_pool_size; //當前buf_pool大小

    buf_chunk_t *chunks; //當前buf_pool中包含的chunks

    hash_table_t *page_hash; //快速檢索已經快取的Page

    UT_LIST_BASE_NODE_T(buf_page_t) free; //空閒Page連結串列

    UT_LIST_BASE_NODE_T(buf_page_t) LRU; //Page快取連結串列,LRU策略淘汰

    UT_LIST_BASE_NODE_T(buf_page_t) flush_list; //還未Flush磁碟的髒頁儲存連結串列

    BufListMutex XXX_mutex; //各個連結串列的互斥Mutex

    ...

}


(2).

Buffer chunks是每個Buffer Pool Instance中實際的物理儲存塊陣列,一個Buffer Pool Instance中有一個或多個chunk,每個chunk的大小預設為128MB,最小為1MB,且這個值在8.0中時可以動態調整生效的。

Buffer Chunks是最低層的物理塊,在啟動階段從作業系統申請,直到資料庫關閉才釋放。


每個Buffer chunk中包含一個buf_block_t的blocks陣列(即Page)。

blocks陣列中的每個元素buf_block_t是一個資料頁結構體,其中包含了一個指向具體資料頁的*frame指標,以及具體的控制體buf_page_t。



struct buf_block_t { //Page控制體

    buf_page_t page; //這個欄位必須要放到第一個位置,這樣才能使得buf_block_t和buf_page_t的指標進行轉換

    byte *frame; //指向真正儲存資料的Page

    BPageMutex mutex; //block級別的mutex

    ...

}


(3).

class buf_page_t {

...

    page_id_t id; //page id

    page_size_t size; //page 大小

    ib_uint32_t buf_fix_count; //用於併發控制

    buf_io_fix io_fix; //用於併發控制

    buf_page_state state; //當前Page所處的狀態,後續會詳細介紹

    lsn_t newest_modification; //當前Page最新修改lsn

    lsn_t oldest_modification; //當前Page最老修改lsn,即第一條修改lsn

    ...

}


(4).buf_page_state,這是一個列舉型別,標識了每個Page所處的狀態,在讀取和訪問時都會對應不同的狀態轉換。

enum buf_page_state {

  BUF_BLOCK_POOL_WATCH, //註釋是給Purge使用的

  BUF_BLOCK_ZIP_PAGE, //壓縮Page狀態

  BUF_BLOCK_ZIP_DIRTY, //壓縮頁髒頁狀態

  BUF_BLOCK_NOT_USED, //儲存在Free List中的Page

  BUF_BLOCK_READY_FOR_USE, //當呼叫到buf_LRU_get_free_block獲取空閒Page,此時被分配的Page就處於這個狀態

  BUF_BLOCK_FILE_PAGE, //正常被使用的狀態,LRU List中的Page都是這個狀態

  BUF_BLOCK_MEMORY, //用於儲存非使用者資料,比如系統資料的Page處於這個狀態

  BUF_BLOCK_REMOVE_HASH //在Page從LRU List和Flush List中被回收並加入Free List時,需要先從Page_hash中移除,此時Page處於這個狀態

};



2. 頁連結串列

連結串列節點是資料頁的控制體(控制體中有指標指向真正的資料頁),連結串列中的所有節點都有同一的屬性,引入其的目的是方便管理。

以下所有的連結串列中的每個節點都是資料頁控制體(buf_page_t)。


2.1

Free List:如其名,Free List中存放的都是未曾使用的空閒Page,InnoDB需要Page時從Free List中獲取,如果Free List為空,即沒有任何空閒Page,則會從LRU List和Flush List中透過淘汰舊Page和Flush髒Page來回收Page。在InnoDB初始化時,會將Buffer chunks中的所有Page加入到Free List中。


2.2

LRU List:所有從資料檔案中新讀取進來的Page都會快取在LRU List,並透過LRU策略對這些Page進行管理。LRU List實際劃分為Young和Old兩個部分,其中Young區儲存的是較熱的資料,Old區儲存的是剛從資料檔案中讀取出來的資料,如果LRU List的長度小於512,則不會將其拆分為Young和Old區。當InnoDB讀取Page時,首先會從當前Buffer Pool Instance的page_hash查詢,並分為三種情況來處理:


如果在page_hash找到,即Page在LRU List中,則會判斷Page是在Old區還是Young區,如果是在Old區,在讀取完Page後會把它新增到Young區的連結串列頭部.

如果在page_hash找到,並且Page在Young區,需要判斷Page所在Young區的位置,只有Page處於Young區總長度大約1/4的位置之後,才會將其新增到Young區的連結串列頭部。

如果未能在page_hash找到,則需要去資料檔案中讀取Page,並將其新增到Old區的頭部。

LRU List採用非常精細的LRU淘汰策略來管理Page,並且用以上機制避免了頻繁對LRU 連結串列的調整。


2.3

Flush List:所有被修改過且還沒來得及被flush到磁碟上的Page(髒頁),都會被儲存在這個連結串列中。所有儲存在Flush List上的資料都會在LRU List中,但在LRU List中的資料不一定都在Flush List中。在Flush List上的每個Page都會儲存其最早修改的lsn,即oldest_modification,雖然一個Page可能被修改多次,但只記錄最早的修改。Flush List上的Page會按照其各自的oldest_modification進行降序排序,連結串列尾部儲存oldest_modification最小的Page,在需要從Flush List中回收Page時,從尾部開始回收。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25380026/viewspace-2747123/,如需轉載,請註明出處,否則將追究法律責任。

相關文章