MySQL系列:innodb原始碼分析之記憶體管理

來源:互聯網
上載者:User

標籤:大致   合并   empty   管理系統   post   推斷   standard   line   機器   

在innodb中實現了自己的記憶體池系統和記憶體堆分配系統,在innodb的記憶體管理系統中,大致分為三個部分:基礎的記憶體塊分配管理、記憶體夥伴分配器和記憶體堆分配器。innodb定義和實現記憶體池的主要目的是提供記憶體的使用率和效率,防止記憶體片段和記憶體配置跟蹤和調試。我們先來看看他們的關係和結構。

下面是它的關係結構圖:


中的:

ut_mem_block塊是基礎記憶體管理

Buddy allocator是記憶體夥伴分配器

    mem_heap是記憶體堆分配器

1.基礎記憶體管理innodb中的記憶體配置和記憶體釋放是通過統一的結構進行管理,詳細的實如今ut0mem.h和ut0mem.c其中。其中最重要的就是對malloc和free的封裝。

通過一個鏈表結構體來管理已經分配的記憶體。結構體例如以下:

 typedef ut_mem_block_struct {            ulint        size;                                   /*這個被分配block的記憶體大小*/            ulint        magic_n;                                /*節點魔法字,用於校正所用*/            UT_LIST_NODE_T(ut_mem_block_t) mem_block_list;       /*block list node,指定prev node和next node*/  };
關於block的list定義是個全域的變數。UT_LIST_BASE_NODE_T(ut_mem_block_t) ut_mem_block_list;全部分配的block都會增加到這個list其中。

在ut_malloc_low函數分配記憶體的時候會將分配的block增加到list其中。在ut_free的時候會所釋放的記憶體所在的block從list其中刪除。

除了這兩個函數以外,innodb還提供ut_free_all_mem函數來釋放全部分配的block和統計分配記憶體的總數ut_total_allocated_memory功能。


 基礎記憶體管理的方法例如以下:
        ut_malloc_low                    分配一個n長度的記憶體塊,並將分配的塊記錄到ut_mem_block_list其中.
        ut_malloc                            與ut_malloc_low功能同樣,可是會用0初始化所分配的記憶體。
        ut_free                                釋放一個分配的記憶體塊,並將其從ut_mem_block_list其中刪除。


        ut_free_all_mem                 釋放ut_mem_block_list全部的記憶體塊並清空ut_mem_block_list
以上函數是支援多線程並行作業的。也就是說是安全執行緒的。 innodb這樣做的目的是保證全部malloc出去的記憶體都在 ut_mem_block_list其中,以便管理。    基礎記憶體管理的結構例如以下:
2.夥伴分配器 innodb的夥伴分配器是基於ut_malloc_low函數之上的記憶體管理器,在建立夥伴分配器時,innodb會一下用ut_malloc_low開闢一個非常大的記憶體塊,然後用夥伴分配來分配這個塊的記憶體使用量。

innodb的夥伴分配器是基於2的基數為基礎的管理方式,其buddy alloc pool的定義例如以下:

   struct mem_pool_struct    {           byte*               buf;                          /*總體記憶體的控制代碼*/        ulint                size;                        /*總體記憶體大小*/        ulint                reserved;                    /*當前分配出去的總記憶體大小*/        mutex             mutex;                          /*多線程相互排斥量*/        UT_LIST_BASE_NODE_T(mem_area_t) free_list[64];    /*area_t鏈表數組,每一個數組單元能管理2的i次方記憶體塊列表,i是數組的下標*/    };
   struct mem_area_struct    {         ulintsize_and_free;                                        /*area的記憶體大小(一定是2的次方),最後一個bit表示是否已經釋放*/         UT_LIST_NODE_T(mem_area_t) free_list;       /*area鏈表的上下area,由於buddy area是會分裂的,有可能多個*/     };
mem_area_t是一個buddy的記憶體地區。也就是mem_area_struct。

下面是一個32位機器管理1024位元組記憶體塊的buddy list分布:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveXVhbnJ4ZHU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" >
每個area是有mem_area_t頭和可分配的記憶體(memory_buffer)確定的,memory_buffer的長度不小於mem_area_t頭的長度,在32位機器上mem_area_t的頭應該是16個位元組(8位元組對齊)。



2.1mem_area_t的分裂  在記憶體配置的過程中,有可能會造成mem_area_t的分裂。還是以上面的範例來說。增加我們要分配一個200位元組的記憶體,這時候夥伴分配器的分配流程是這種:        1.找到一個離200+sizeof(mem_area_t)近期的2的i次方的數(256),確定i = 8。        2.在free_list[i]的列表中尋找是否有空暇的node,假設有,將node職位no free.假設沒有。對i + 1層運行尋找是否有可用的記憶體。        3.在上面的範例中,i+1=9,free_list是空的,繼續在i+2層找,一次類推。直到找到有node的層,也就是i = 10;        4.首先對10層進行分裂。分裂成兩512大小的第9層節點,並從10刪除area。在第9層增加2個512的node.        5.然後在對第9層的第一個節點進行分裂,分裂兩個大小為256的第8層節點,並從第九層刪除,在第8層增加2個節點。        6.將第一個256大小的area分配給相應的操作者,共置為no free標識。

    下面是分配了一個200位元組的記憶體池結構:
假設分配出去後的area_t會從free_list[i]鏈表中刪除,也就是說在buddy上將是記錄的。
2.2mem_area_t的合并假設200位元組分配出去後,使用完成會歸還給buddy allocator,還是拿上面的範例來說。就會發生area合并的情況,過程例如以下:
    1.使用者歸還夥伴分配的記憶體,首先會依據area_t的資訊去找到自己的buddy,也就是第8層另外一個沒有被分配的area.
    2.找到buddy area後。推斷buddy area是否是釋放狀態,假設是,觸發合并。將自己和buddy area從第8層刪除,合并成一個512大小的第9層area,
    3.在反覆1 ~ 2步,又會將自己和第九層另外一個buddy area合并成一個1024大小的第10層area.

2.3buddy allocator的介面函數:    mem_pool_create                構建一個buddy allocator
    mem_area_alloc                   用buddy allocator分配一塊記憶體
    mem_area_free                    將一塊記憶體歸還給buddy allocator
    mem_pool_get_reserved      獲得buddy allocator已經使用的記憶體大小

3記憶體配置堆(memory heap)innodb中的記憶體管理終於的體現形式是mem_heap_t記憶體配置與管理,全部關於記憶體配置的操作都會調用mem_heap的API方法,mem_heap_t的結構定義例如以下:

struct mem_block_info_struct{     ulint magic_n;         /*魔法字*/     char file_name[8];    /*分配記憶體的檔案*/     ulint line;            /*分配記憶體的檔案所在行*/     ulint len;             /*block的長度*/     ulint type;            /*依賴的底層配置類型,有DYNAMIC、BUFFER、BTR_SEARCH三種類型*/     ibool init_block; /*是否是外部分配的記憶體塊*/     ulint free;           /*被佔用的空間大小*/     ulint start;         /*可分配記憶體的起始位置*/     byte* free_block;     /*備用block,只在BTR_SEARCH方式可用*/     UT_LIST_BASE_NODE_T(mem_block_t)base;     UT_LIST_NODE_T(mem_block_t) list;};
備忘:mem_block_info_struct/mem_block_info_t/mem_block_t/mem_heap_t是等價
mem_heap_t的記憶體結構例如以下:


關於mem_heap_t的幾個要點:
    1.一個mem_block_t最小空間不小於64位元組,標準的大小是8KB,在非MEM_HEAP_BUFFER模式下分配的空間不大於page size - 200(page size一般為16KB)

    2.mem_heap_t有三種類型,各自是DYNAMIC、BUFFER、BTR_SEARCH。在DYNAMIC模式下都是基於buddy allocator進行mem_block_t分配的。在BTR_SEARCH模式下,使用free_block來作為記憶體配置,在BUFFER模式下比較複雜。假設分配的記憶體大小< page size的一半時,使用buddy alloc,否則使用buf_frame的記憶體配置方式(這個是屬於buf0buf.XX裡面的方式。還未開始分析)。

    3.mem_heap_t在分配新的mem_block_t的時候一定是分配一個heap最後節點大小的兩倍,假設分配的大小超過MEM_MAX_ALLOC_IN_BUF(相當於一個page size)的時候,heap 類型推斷,在不是DYNAMIC模式下。最大就是一個MEM_MAX_ALLOC_IN_BUF大小。假設其它模式下就是設定成MEM_BLOCK_STANDARD_SIZE標準大小。在這些限制外,假設須要分配的記憶體大於這些限制,以分配記憶體大小為準進行mem_block_t分配。分配好的mem_block_t總是增加到heap base list的最後,也就是heap堆棧的頂端。



    4.mem_heap_t在釋放mem_block_t時候總是從頂端開始釋放,直到不能釋放為止(mem_block_t沒有被使用者歸還)。在mem_block_t釋放的時候也是須要參考DYNAMIC、BUFFER、BTR_SEARCH類型進行相對於的歸還規則(和2要點是相相應的)。



mem_heap_t函數方法說明:
mem_heap_create                                        用DYNAMIC模式建立一個mem_heap_t
mem_heap_create_in_buffer                        用BUFFER模式建立一個mem_heap_t
mem_heap_create_in_btr_search                 用BTR_SEARCH模式建立一個mem_heap_t
mem_heap_free                                            釋放mem_heap_t對象
mem_alloc                                                    建立在MEM_HEAP_DYNAMIC模式下。並分配一塊指定大小的記憶體(在這樣的方式下mem_heap_t僅僅會有一個mem_block_t)
mem_free                                                      歸還mem_heap_t分配的記憶體,並釋放mem_heap_t
mem_heap_alloc                                           在指定的mem_heap_t上分配一塊記憶體
mem_heap_get_heap_top                            獲得heap頂端塊可使用記憶體的地址
mem_heap_empty                                        清空指定的mem_heap_t
mem_heap_get_top                                     獲得heap頂部的指定n大小的mem_block_t指標
mem_heap_free_top                                    釋放heap頂部N大小的mem_block_t塊

4總結innodb提供記憶體池和heap分配方式來統一管理記憶體,最基本的目的是提高記憶體的率。

在MySQL-5.6的版本號碼中。innodb提供兩種選擇,一種是使用innodb提供的記憶體池管理記憶體,另一種是提供系統的malloc和free來作為記憶體管理。MySQL預設的是系統管理記憶體方式,一些有經驗的DBA會使用系統的管理記憶體方式+TMalloc來做記憶體最佳化。藉助TMalloc高效的記憶體管理方式實現MySQL的效能提升。





MySQL系列:innodb原始碼分析之記憶體管理

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.