日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

MySQL • 源码分析 • 内存分配机制

發布時間:2025/3/20 数据库 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MySQL • 源码分析 • 内存分配机制 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

摘要:前言 內存資源由操作系統管理,分配與回收操作可能會執行系統調用(以 malloc 算法為例,較大的內存空間分配接口是 mmap, 而較小的空間 free 之后并不歸還給操作系統 ),頻繁的系統調用必然會降低系統性能,但是可以最大限度的把使用完畢的內存讓給其它進程使用,相反長時間占有內存資源可以減少系統調用次數,但是內存資源不足會導致操作系統頻繁換頁,降低服務器的整體性能。

前言

內存資源由操作系統管理,分配與回收操作可能會執行系統調用(以 malloc 算法為例,較大的內存空間分配接口是 mmap, 而較小的空間 free 之后并不歸還給操作系統 ),頻繁的系統調用必然會降低系統性能,但是可以最大限度的把使用完畢的內存讓給其它進程使用,相反長時間占有內存資源可以減少系統調用次數,但是內存資源不足會導致操作系統頻繁換頁,降低服務器的整體性能。

數據庫是使用內存的“大戶”,合理的內存分配機制就尤為重要,上一期月報介紹了 PostgreSQL 的內存上下文,本文將介紹在 MySQL 中又是怎么管理內存的。

基礎接口封裝

MySQL 在基本的內存操作接口上面封裝了一層,增加了控制參數 my_flags

void *my_malloc(size_t size, myf my_flags) void *my_realloc(void *oldpoint, size_t size, myf my_flags) void my_free(void *ptr)復制代碼

my_flags 的值目前有:

MY_FAE /* Fatal if any error */ MY_WME /* Write message on error */ MY_ZEROFILL /* Fill array with zero */復制代碼

MY_FAE 表示內存分配失敗就退出整個進程,MY_WME 表示內存分配失敗是否需要記錄到日志中,MY_ZEROFILL 表示分配內存后初始化為0。

MEM_ROOT

基本結構

在 MySQL 的 Server 層中廣泛使用 MEM_ROOT 結構來管理內存,避免頻繁調用封裝的基礎接口,也可以統一分配和管理,防止發生內存泄漏。不同的 MEM_ROOT 之間互相沒有影響,不像 PG 中不同的內存上下文之間還有關聯。這可能得益于 MySQL Server 層是面向對象的代碼,MEM_ROOT 作為類中的一個成員變量,伴隨著對象的整個生命周期。比較典型的類有: THD,String, TABLE, TABLE_SHARE, Query_arena, st_transactions 等。

MEM_ROOT 分配內存的單元是 Block,使用 USED_MEM 結構體來描述。結構比較簡單,Block 之間相互連接形成內存塊鏈表,left 和 size 表示對應 Block 還有多少可分配的空間和總的空間大小。

typedef struct st_used_mem { /* struct for once_alloc (block) */ struct st_used_mem *next; /* Next block in use */ unsigned int left; /* memory left in block */ unsigned int size; /* size of block */ } USED_MEM;復制代碼

而 MEM_ROOT 結構體負責管理 Block 鏈表 :

typedef struct st_mem_root {USED_MEM *free; /* blocks with free memory in it */USED_MEM *used; /* blocks almost without free memory */USED_MEM *pre_alloc; /* preallocated block */ /* if block have less memory it will be put in 'used' list */size_t min_malloc;size_t block_size; /* initial block size */ unsigned int block_num; /* allocated blocks counter */ /* first free block in queue test counter (if it exceed MAX_BLOCK_USAGE_BEFORE_DROP block will be dropped in 'used' list)*/ unsigned int first_block_usage;void (*error_handler)(void); } MEM_ROOT;復制代碼

整體結構就是兩個 Block 鏈表,free 鏈表管理所有的仍然存在可分配空間的 Block,used 鏈表管理已經沒有可分配空間的所有 Block。pre_alloc 類似于 PG 內存上下文中的 keeper,在初始化 MEM_ROOT 的時候就可以預分配一個 Block 放到 free 鏈表中,當 free 整個 MEM_ROOT 的時候可以通過參數控制,選擇保留 pre_alloc 指向的 Block。min_malloc 控制一個 Block 剩余空間還有多少的時候從 free 鏈表移除,加入到 used 鏈表中。block_size 表示初始化 Block 的大小。block_num 表示 MEM_ROOT 管理的 Block 數量。first_block_usage 表示 free 鏈表中第一個 Block 不滿足申請空間大小的次數,是一個調優的參數。err_handler 是錯誤處理函數。

分配流程

使用 MEM_ROOT 首先需要初始化,調用 init_alloc_root, 通過參數可以控制初始化的 Block 大小和 pre_alloc_size 的大小。其中比較有意思的點是 min_block_size 直接指定一個值 32,個人覺得不太靈活,對于小內存的申請可能會有比較大的內存碎片。另一個是 block_num 初始化為 4,這個和決定新分配的 Block 大小策略有關。

void init_alloc_root(MEM_ROOT *mem_root, size_t block_size,size_t pre_alloc_size __attribute__((unused))) {mem_root->free= mem_root->used= mem_root->pre_alloc= 0;mem_root->min_malloc= 32;mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;mem_root->error_handler= 0;mem_root->block_num= 4; /* We shift this with >>2 */mem_root->first_block_usage= 0;if (pre_alloc_size){if ((mem_root->free= mem_root->pre_alloc=(USED_MEM*) my_malloc(pre_alloc_size+ ALIGN_SIZE(sizeof(USED_MEM)),MYF(0)))){mem_root->free->size= pre_alloc_size+ALIGN_SIZE(sizeof(USED_MEM));mem_root->free->left= pre_alloc_size;mem_root->free->next= 0;rds_update_query_size(mem_root, mem_root->free->size, 0);}}DBUG_VOID_RETURN; }復制代碼

初始化完成就可以調用 alloc_root 進行內存申請,整個分配流程并不復雜,代碼也不算長,為了方便閱讀貼出來,也可以略過直接看分析。

void *alloc_root( MEM_ROOT *mem_root, size_t length ) {size_t get_size, block_size;uchar * point;reg1 USED_MEM *next = 0;reg2 USED_MEM **prev;length = ALIGN_SIZE( length );if ( (*(prev = &mem_root->free) ) != NULL ) // 判斷 free 鏈表是否為空{if ( (*prev)->left < length &&mem_root->first_block_usage++ >= ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP &&(*prev)->left < ALLOC_MAX_BLOCK_TO_DROP ) // 優化策略{next = *prev;*prev = next->next; /* Remove block from list */next->next = mem_root->used;mem_root->used = next;mem_root->first_block_usage = 0;}// 找到一個空閑空間大于申請內存空間的 Block for ( next = *prev; next && next->left < length; next = next->next )prev = &next->next;}if ( !next ) // free 鏈表為空,或者沒有滿足可分配條件 Block{ /* Time to alloc new block */block_size = mem_root->block_size * (mem_root->block_num >> 2);get_size = length + ALIGN_SIZE( sizeof(USED_MEM) );get_size = MY_MAX( get_size, block_size );if ( !(next = (USED_MEM *) my_malloc( get_size, MYF( MY_WME | ME_FATALERROR ) ) ) ){if ( mem_root->error_handler )(*mem_root->error_handler)();DBUG_RETURN( (void *) 0 ); /* purecov: inspected */}mem_root->block_num++;next->next = *prev;next->size = get_size;next->left = get_size - ALIGN_SIZE( sizeof(USED_MEM) ); *prev = next; // 新申請的 Block 放到 free 鏈表尾部}point = (uchar *) ( (char *) next + (next->size - next->left) );if ( (next->left -= length) < mem_root->min_malloc ) // 分配完畢后,Block 是否還能在 free 鏈表中繼續分配{ /* Full block */*prev = next->next; /* Remove block from list */next->next = mem_root->used;mem_root->used = next;mem_root->first_block_usage = 0;} }復制代碼

首先判斷 free 鏈表是否為空,如果不為空,按邏輯應該遍歷整個鏈表,找到一個空閑空間足夠大的 Block,但是看代碼是先執行了一個判斷語句,這其實是一個空間換時間的優化策略,因為free 鏈表大多數情況下都是不為空的,幾乎每次分配都需要從 free 鏈表的第一個 Block 開始判斷,我們當然希望第一個 Block 可以立刻滿足要求,不需要再掃描 free 鏈表,所以根據調用端的申請趨勢,設置兩個變量:ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP 和 ALLOC_MAX_BLOCK_TO_DROP,當 free 鏈表的第一個 Block 申請次數超過 ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP 而且剩余的空閑空間小于 ALLOC_MAX_BLOCK_TO_DROP,就把這個 Block 放到 used 鏈表里,因為它已經一段時間無法滿足調用端的需求了。

如果在 free 鏈表中沒有找到合適的 Block,就需要調用基礎接口申請一塊新的內存空間,新的內存空間大小當然至少要滿足這次申請的大小,同時預估的新 Block 大小是 : mem_root->block_size * (mem_root->block_num >> 2) 也就是初始化的 Block 大小乘以當前 Block 數量的 1/4,所以初始化 MEM_ROOT 的 block_num 至少是 4。

找到合適的 Block 之后定位到可用空間的位置就行了,返回之前最后需要判斷 Block 分配之后是否需要移動到 used 鏈表。

歸還內存空間的接口有兩個:mark_blocks_free(MEM_ROOT *root)和 free_root(MEN_ROOT *root,myf MyFlags) ,可以看到兩個函數的參數不像基礎封裝的接口,沒有直接傳需要歸還空間的指針,傳入的是 MEM_ROOT 結構體指針,說明對于 MEM_ROOT 分配的內存空間,是統一歸還的。mark_blocks_free 不真正的歸還 Block,而是放到 free 鏈表中標記可用。free_root 真正歸還空間給操作系統,MyFlages 可以控制是否和標記刪除的函數行為一樣,也可以控制 pre_alloc 指向的 Block 是否歸還。

總結

  • 從空間利用率上來講,MEM_ROOT 的內存管理方式在每個 Block 上連續分配,內部碎片基本在每個 Block 的尾部,由 min_malloc 成員變量和參數 ALLOC_MAX_BLOCK_USAGE_BEFORE_DROP,ALLOC_MAX_BLOCK_TO_DROP 共同決定和控制,但是 min_malloc 的值是在代碼中寫死的,有點不夠靈活,可以考慮寫成可配置的,同時如果寫超過申請長度的空間,就很有可能會覆蓋后面的數據,比較危險。但相比 PG 的內存上下文,空間利用率肯定是會高很多的。
  • 從時間利用率上來講,不提供 free 一個 Block 的操作,基本上一整個 MEM_ROOT 使用完畢才會全部歸還給操作系統,可見 MySQL 在內存上面還是比較“貪婪”的。
  • 從使用方式上來講,因為 MySQL 擁有多個存儲引擎,引擎之上的 Server 層是面向對象的 C++ 代碼,MEM_ROOT 常常作為對象中的一個成員變量,在對象的生命周期內分配內存空間,在對象析構的時候回收,引擎的內存申請使用封裝的基本接口。相比之下 MySQL 的使用方式更加多元,PG 的統一性和整體性更好。
版權聲明:本文內容由互聯網用戶自發貢獻,本社區不擁有所有權,也不承擔相關法律責任。如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至:yqgroup@service.aliyun.com 進行舉報,并提供相關證據,一經查實,本社區將立刻刪除涉嫌侵權內容。

原文鏈接


轉載于:https://juejin.im/post/5a30d34b6fb9a045055e1eb4

總結

以上是生活随笔為你收集整理的MySQL • 源码分析 • 内存分配机制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 色爱区综合 | 中文天堂在线视频 | 日韩毛片中文字幕 | 一区二区不卡在线观看 | 国产在线精 | 欧美30p| 欧美日韩一区二区在线 | 国产综合无码一区二区色蜜蜜 | 天天干网站 | 粉嫩小泬无遮挡久久久久久 | 亚洲激情视频小说 | 日韩一区免费 | 超碰公开在线观看 | 日本熟妇人妻xxxxx | 色图社区 | 成人免费看毛片 | 视频一区二区三区精品 | 欧美整片在线观看 | 久久国产中文 | 午夜神器在线观看 | 欧美日韩大陆 | 久久国产劲爆∧v内射 | 日本三级韩国三级美三级91 | 一本色道久久加勒比精品 | 欧美性猛交aaaa片黑人 | 91福利专区 | 91中文字幕网 | 另类小说婷婷 | 潘金莲一级淫片免费放动漫 | 韩国中文字幕hd久久精品 | 动漫一区二区三区 | 免费在线视频你懂的 | 影音先锋欧美在线 | 少女与动物高清版在线观看 | 中国女人毛茸茸 | 亚洲综合在线成人 | 国产在线拍揄自揄拍 | 中文字幕日韩有码 | 被灌满精子的波多野结衣 | 人妻视频一区 | 国产综合视频一区二区 | 精品国产18久久久久久二百 | 日韩簧片 | 成人黄色一区二区三区 | 美女扒开内裤让男人桶 | 黄色三级视频在线观看 | 嫩草视频入口 | 欧美日韩国产亚洲沙发 | 尤物av在线 | 女生喷水视频 | 亚洲视频在线看 | 操色网 | 91精品免费在线 | 一区二区三区四区高清视频 | 午夜av毛片 | 久久久夜| 亚洲AV无码一区二区伊人久久 | 亚洲天堂岛 | 豆花视频在线 | 亚洲每日在线 | 国产精品羞羞答答在线观看 | 无码视频一区二区三区 | 欧美xxxx性xxxxx高清 | 黄大色黄大片女爽一次 | 91国内在线| 都市激情亚洲 | 岛国大片在线免费观看 | 男人插女人下面视频 | 一区二区在线观看免费 | 日韩av毛片在线观看 | 亚洲人人爽 | 欧美日本综合 | 欧美成年人视频在线观看 | 91插视频 | 中文字幕在线第一页 | 男人插女人免费视频 | 欧美少妇毛茸茸 | 国产伦乱视频 | 天天射夜夜 | 在线色图| 欧洲最强rapper网站直播 | 九色91popny蝌蚪新疆 | 国产精品久久久久久中文字 | 精品人妻无码一区二区三 | 成年人的视频网站 | 色老头一区二区三区在线观看 | 摸摸大奶子 | 天天干人人 | 久久久久久蜜桃 | 一二三区在线播放 | 久久一本综合 | 在线cao | 粉嫩av一区二区三区天美传媒 | 亚洲一区二区三区四区五区午夜 | 福利在线电影 | 黄网站色| 神马久久久久久久久 | 日本美女一级视频 | 国产综合亚洲精品一区二 |