本文主要介紹在ARM64 CentOS系統(tǒng)下,MySQL使用jemalloc作為內(nèi)存管理器時(shí),內(nèi)存占用問題的分析過程和解決方法。
Jemalloc 簡介
Jemalloc是由Jason Evans在FreeBSD項(xiàng)目中引入的內(nèi)存分配管理器,它的優(yōu)勢(shì)在于減少內(nèi)存碎片和提升高并發(fā)場景下內(nèi)存的分配效率。
Jemalloc中的基本概念和數(shù)據(jù)結(jié)構(gòu)
-
size_class: 每個(gè) size_class 代表 jemalloc 分配的內(nèi)存大小,共有 NSIZES(232)個(gè)小類(如果用戶申請(qǐng)的大小位于兩個(gè)小類之間,會(huì)取較大的,比如申請(qǐng)14字節(jié),位于8和16字節(jié)之間,按16字節(jié)分配),分為2大類:small_class和large_class
-
Base: 用于分配 jemalloc 元數(shù)據(jù)內(nèi)存的結(jié)構(gòu),通常一個(gè) base 大小為 2mb, 所有 base 組成一個(gè)鏈表。
-
bin: 管理正在使用中的 slab(即用于小內(nèi)存分配的 extent) 的集合,每個(gè) bin 對(duì)應(yīng)一個(gè) size_class
-
extent: 管理 jemalloc 內(nèi)存塊(即用于用戶分配的內(nèi)存)的結(jié)構(gòu),每一個(gè)內(nèi)存塊大小可以是 N*page_size(N >= 1)。
-
slab: 當(dāng) extent 用于分配 small_class 內(nèi)存時(shí),稱其為 slab。一個(gè) extent 可以被用來處理多個(gè)同一size_class 的內(nèi)存申請(qǐng)。
-
extents: 管理 extent 的集合。
-
arena: 用于分配&回收 extent 的結(jié)構(gòu),每個(gè)用戶線程會(huì)被綁定到一個(gè) arena 上,默認(rèn)每個(gè)邏輯 CPU 會(huì)有 4 個(gè) arena 來減少鎖的競爭,各個(gè) arena 所管理的內(nèi)存相互獨(dú)立。
-
rtree: 全局唯一的存放每個(gè) extent 信息的 Radix Tree
-
cache_bin: 每個(gè)線程獨(dú)有的用于分配小內(nèi)存的緩存
-
tcache: 每個(gè)線程獨(dú)有的緩存(Thread Cache),大多數(shù)內(nèi)存申請(qǐng)都可以在 tcache 中直接得到,從而避免加鎖
-
tsd: Thread Specific Data,每個(gè)線程獨(dú)有,用于存放與這個(gè)線程相關(guān)的結(jié)構(gòu)

MySQL使用Jemalloc
鑒于jemalloc的諸多優(yōu)點(diǎn),計(jì)劃使用jemalloc作為內(nèi)存管理器來優(yōu)化MySQL,下面是測試環(huán)境。測試環(huán)境
CPU: ARM64Memory: 512GB
OS: CentOS Linux release 8.3.2011
Kernel: 4.18.0-193.28.1.el8_2.aarch64
MySQL: 8.0.25
Test Tool: SysBench 1.0.20
Jemalloc: 5.2.1
jemalloc的安裝和使用
# wget https://github.com/jemalloc/jemalloc/archive/refs/tags/5.2.1.tar.gz -O jemalloc-5.2.1.tar.gz # tar xzvf jemalloc-5.2.1.tar.gz # cd jemalloc-5.2.1 # ./autogen.sh //安裝到指定目錄 # ./configure --prefix=/home/test-user/jemalloc-5.2.1-install //編譯并安裝 # make; make install //配置環(huán)境變量 # export LD_PRELOAD=/home/test-user/jemalloc-5.2.1-install/lib/libjemalloc.so安裝好MySQL后,通過如下命令檢查jemalloc是否被正常使用(MySQL的安裝請(qǐng)參考官方步驟,這里不再贅述)# lsof -n |grep jemalloc下圖顯示MySQL已經(jīng)正常使用jemalloc 
測試用例
sysbench啟動(dòng)80個(gè)線程對(duì)MySQL進(jìn)行讀寫壓測。異常問題
壓測過程中發(fā)現(xiàn)內(nèi)存使用“異?!保篗ySQL進(jìn)程占用的物理內(nèi)存超過了100GB。
?不使用jemalloc切換回默認(rèn)的glibc后,內(nèi)存占用降低到了7GB,和以往的測試結(jié)果一致。
?從測試結(jié)果看,使用jemalloc作為內(nèi)存管理器時(shí)內(nèi)存使用量激增,需要進(jìn)一步分析原因。內(nèi)存使用量是否合理?是否和架構(gòu)相關(guān)? 問題分析
第一階段分析
1. 復(fù)現(xiàn)“問題”
首先需要確定該“問題”是否在x86架構(gòu)上也存在,是否和操作系統(tǒng)或內(nèi)核版本相關(guān)。為了快速驗(yàn)證以上疑問,在AWS上分別創(chuàng)建x86實(shí)例(m5)和arm64實(shí)例(m6g)進(jìn)行測試, 并沒有復(fù)現(xiàn)“問題”。這兩個(gè)實(shí)例默認(rèn)的操作系統(tǒng)是Amazon Linux 2,而本地測試時(shí)使用的是CentOS8,在m6g上安裝CentOS8重新測試,“問題”復(fù)現(xiàn)。測試結(jié)果如下:
2. 對(duì)比分析
對(duì)比測試環(huán)境,分析它們不同點(diǎn),我們發(fā)現(xiàn)該“問題”只有在內(nèi)核page size是64KB時(shí)才會(huì)出現(xiàn)。另外,根據(jù)前文介紹,jemalloc中extent會(huì)基于page size分配內(nèi)存。而且,深入分析jemalloc代碼后還發(fā)現(xiàn)有多個(gè)數(shù)據(jù)結(jié)構(gòu)的內(nèi)存分配都涉及到page size,比如size_class, bin, extents, arena等等。頁表在操作系統(tǒng)中作為最基礎(chǔ)的內(nèi)存分配結(jié)構(gòu),ARM64支持4K、16K、64K不同大小的頁表,x86只支持4KB。而本地測試使用的ARM64 CentOS的默認(rèn)page size就是64KB,所以初步判斷該“問題”和page size的配置相關(guān)。3. 解決方法
即然ARM64架構(gòu)支持多種page size,而page size為4KB時(shí)沒有出現(xiàn)問題,那么可以修改ARM64 CentOS8的內(nèi)核默認(rèn)的page size來解決該"問題"。修改page size方法
由于內(nèi)核當(dāng)前頁表大小只支持靜態(tài)配置,不支持動(dòng)態(tài)修改,所以需要重新編譯內(nèi)核。修改方法如下:-
在https://www.kernel.org/獲取需要的內(nèi)核版本
-
解壓并修改內(nèi)核配置參數(shù)
# tar xf linux-x.x.x.tar.xz # cd linux-x.x.x # cp /boot/config-xxx .config # make menuconfig -
在圖形菜單中找到“Kernel Features-> Page size”,選擇4KB并保存配置

-
編譯并安裝新的內(nèi)核
# make -j # make modules_install # make install -
重啟進(jìn)入新的內(nèi)核,參看page size是否修改成功
# getconf PAGE_SIZE 4096
4. 驗(yàn)證
修改page size為4KB后重新測試,jemalloc內(nèi)存使用量和glibc接近。測試結(jié)果如下:
5. 潛在問題
至此該“問題”似乎可以通過修改page size來解決。但是,如果用戶仍然需要使用64KB的頁表,該方法將不再適用。實(shí)際上,jemalloc本身支持編譯參數(shù)“--with-lg-page=16”,該參數(shù)可以使jemalloc在page size為4KB時(shí)復(fù)用多個(gè)頁面來達(dá)到使用64KB頁面的效果。嘗試在4KB page size的系統(tǒng)下加入該編譯參數(shù),并沒有出現(xiàn)內(nèi)存使用量激增的現(xiàn)象。這說明除了page size,還有其他因素影響了jemalloc的內(nèi)存分配,仍然需要進(jìn)一步分析。第二階段分析
1. micro-benchmark
通過以上測試發(fā)現(xiàn)該“問題”和MySQL并沒有直接關(guān)系。為了簡化分析和復(fù)現(xiàn)過程,單獨(dú)開發(fā)了一個(gè)micro-benchmarkhttps://github.com/machuang1983/jemalloc_micro_benchmark該程序用于建立多個(gè)線程,每個(gè)線程分配一定內(nèi)存,程序運(yùn)行過程中實(shí)時(shí)打印進(jìn)程的內(nèi)存使用情況。通過micro-benchmark可以快速復(fù)現(xiàn)問題。測試結(jié)果顯示,每新建一個(gè)線程就會(huì)消耗1GB左右的內(nèi)存。測試結(jié)果如下:
?再次簡化測試,直接運(yùn)行單線程程序,如sleep 100,進(jìn)程就會(huì)占用1GB內(nèi)存。
?由此看見,jemalloc針對(duì)一個(gè)線程進(jìn)行內(nèi)存初始化分配時(shí)就會(huì)分配1GB內(nèi)存。需要深入分析jemalloc具體的分配機(jī)制。 2. 深入分析jemalloc代碼
按前文所述,jemalloc的內(nèi)存分配涉及到多個(gè)數(shù)據(jù)結(jié)構(gòu),我們結(jié)合gdb單步執(zhí)行來分析jemalloc代碼,同時(shí)實(shí)時(shí)查看內(nèi)存占用的變化,由此定位到關(guān)鍵代碼。調(diào)試過程中發(fā)現(xiàn),base會(huì)基于默認(rèn)的hugepage size分配內(nèi)存,分配之后監(jiān)控到內(nèi)存使用量突然增大,具體代碼在https://github.com/jemalloc/jemalloc/blob/dev/src/base.c#L46-L49
?繼續(xù)搜索hugepage size相關(guān)代碼,還發(fā)現(xiàn)另一處使用它來分配內(nèi)存,代碼在https://github.com/jemalloc/jemalloc/blob/master/src/arena.c#L2052
?由此可見除了page size,hugepage size對(duì)jemalloc的內(nèi)存分配也有影響。通常hugepage size比page size大得多,所以hugepage size的影響會(huì)更大。 3. hugepage
內(nèi)存管理采用"分頁機(jī)制",但是當(dāng)運(yùn)行內(nèi)存需求量較大時(shí),默認(rèn)page大小的頁面會(huì)導(dǎo)致較多的TLB miss和缺頁中斷,從而大大影響應(yīng)用程序性能。所以,有些場景希望可以使用更大的內(nèi)存頁作為映射單位,因此引入了hugepage。不同架構(gòu)支持的hugepage size不同,見下表:
4. 解決方法
ARM64 CentOS在page size=64KB時(shí),默認(rèn)hugepage size是512MB,jemalloc的base會(huì)以512MB來分配內(nèi)存,而當(dāng)page size=4KB時(shí),默認(rèn)hugepage size是2MB。所以回顧前面的測試,修改page size后問題消失的主要原因是默認(rèn)的hugepage size改變導(dǎo)致的。默認(rèn)hugepage size修改方法
- 修改啟動(dòng)參數(shù)“default_hugepagesz=2M” ARM64支持多種hugepage size,可以使用hugepagesz啟動(dòng)參數(shù)進(jìn)行調(diào)整,無需重新編譯內(nèi)核。
- 內(nèi)核啟動(dòng)時(shí),輸入"e"進(jìn)入修改啟動(dòng)選項(xiàng)界面,加入?yún)?shù)“default_hugepagesz=2M”,然后輸入"ctrl+x"啟動(dòng)內(nèi)核。
- Centos: Set default_hugepagesz=2M in /boot/grub2/grubenv file
- Ubuntu: Set default_hugepagesz=2M to GRUB_CMDLINE_LINUX in /etc/default/grub file, then run “update-grub”
- 永久修改
- 臨時(shí)修改
- jemalloc編譯參數(shù)"--with-lg-hugepage=21" jemalloc支持編譯參數(shù)"--with-lg-hugepage=21",替代系統(tǒng)的默認(rèn)的hugepage size為2MB。建議使用該方法。
5. 驗(yàn)證
修改默認(rèn)hugepage size后測試結(jié)果(sysbench使用256線程壓測)如下:
?測試結(jié)果顯示,將hugepage size改為2MB以后,jemalloc的內(nèi)存使用情況和glibc接近。 總結(jié)
該"問題"和架構(gòu)無關(guān),jemalloc作為內(nèi)存管理器,如果默認(rèn)hugepage size較大,會(huì)導(dǎo)致軟件占用較大的內(nèi)存,jemalloc提供了編譯參數(shù)"--with-lg-hugepage=21"來降低這個(gè)影響。由于ARM64支持更多類型的page size和hugepage size,用以提升軟件的性能。所以用戶在ARM64系統(tǒng)上使用jemalloc時(shí),需要關(guān)注默認(rèn)的page size和hugepage size,并根據(jù)具體需求做出相應(yīng)的調(diào)整。審核編輯 :李倩
-
管理器
+關(guān)注
關(guān)注
0文章
265瀏覽量
19527 -
MySQL
+關(guān)注
關(guān)注
1文章
906瀏覽量
29560 -
線程
+關(guān)注
關(guān)注
0文章
509瀏覽量
20829 -
malloc
+關(guān)注
關(guān)注
0文章
53瀏覽量
387
原文標(biāo)題:技術(shù)分享 | Arm64 CentOS系統(tǒng)下MySQL使用jemalloc時(shí)的問題和解決方法
文章出處:【微信號(hào):Ithingedu,微信公眾號(hào):安芯教育科技】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
MAX16065/MAX16066:多功能系統(tǒng)管理器的深度剖析
深入解析MAX16046/MAX16048:多功能系統(tǒng)管理器的卓越之選
深入解析LTC2980 16通道PMBus電源系統(tǒng)管理器
LTC4110電池備份系統(tǒng)管理器:特性、應(yīng)用與設(shè)計(jì)指南
深入解析LTC4099:高性能USB電源管理器與充電器
LTC4162 - S:高級(jí)鉛酸電池充電器與電源路徑管理器
LTC4410 USB 電源管理器:高效電源管理解決方案
C編譯器錯(cuò)誤與解決方法
【產(chǎn)品介紹】Altair PBS Professional HPC工作負(fù)載管理器和作業(yè)調(diào)度管理系統(tǒng)
K230設(shè)備管理器里面沒有COM是怎么回事?
k230彈出windows資源管理器無法識(shí)別usb設(shè)備怎么解決?
protel 99 se的設(shè)計(jì)管理器找不到
企業(yè)級(jí)MySQL數(shù)據(jù)庫管理指南
ADI創(chuàng)新電源管理器件介紹
MySQL使用jemalloc作為內(nèi)存管理器時(shí)的解決方法
評(píng)論