本文主要介紹在ARM64 CentOS系統下,MySQL使用jemalloc作為內存管理器時,內存占用問題的分析過程和解決方法。
Jemalloc 簡介
Jemalloc是由Jason Evans在FreeBSD項目中引入的內存分配管理器,它的優勢在于減少內存碎片和提升高并發場景下內存的分配效率。
Jemalloc中的基本概念和數據結構
-
size_class: 每個 size_class 代表 jemalloc 分配的內存大小,共有 NSIZES(232)個小類(如果用戶申請的大小位于兩個小類之間,會取較大的,比如申請14字節,位于8和16字節之間,按16字節分配),分為2大類:small_class和large_class
-
Base: 用于分配 jemalloc 元數據內存的結構,通常一個 base 大小為 2mb, 所有 base 組成一個鏈表。
-
bin: 管理正在使用中的 slab(即用于小內存分配的 extent) 的集合,每個 bin 對應一個 size_class
-
extent: 管理 jemalloc 內存塊(即用于用戶分配的內存)的結構,每一個內存塊大小可以是 N*page_size(N >= 1)。
-
slab: 當 extent 用于分配 small_class 內存時,稱其為 slab。一個 extent 可以被用來處理多個同一size_class 的內存申請。
-
extents: 管理 extent 的集合。
-
arena: 用于分配&回收 extent 的結構,每個用戶線程會被綁定到一個 arena 上,默認每個邏輯 CPU 會有 4 個 arena 來減少鎖的競爭,各個 arena 所管理的內存相互獨立。
-
rtree: 全局唯一的存放每個 extent 信息的 Radix Tree
-
cache_bin: 每個線程獨有的用于分配小內存的緩存
-
tcache: 每個線程獨有的緩存(Thread Cache),大多數內存申請都可以在 tcache 中直接得到,從而避免加鎖
-
tsd: Thread Specific Data,每個線程獨有,用于存放與這個線程相關的結構
MySQL使用Jemalloc
鑒于jemalloc的諸多優點,計劃使用jemalloc作為內存管理器來優化MySQL,下面是測試環境。測試環境
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 //配置環境變量 # export LD_PRELOAD=/home/test-user/jemalloc-5.2.1-install/lib/libjemalloc.so
安裝好MySQL后,通過如下命令檢查jemalloc是否被正常使用(MySQL的安裝請參考官方步驟,這里不再贅述)# lsof -n |grep jemalloc
下圖顯示MySQL已經正常使用jemalloc 測試用例
sysbench啟動80個線程對MySQL進行讀寫壓測。異常問題
壓測過程中發現內存使用“異常”:MySQL進程占用的物理內存超過了100GB。 ?不使用jemalloc切換回默認的glibc后,內存占用降低到了7GB,和以往的測試結果一致。 ?從測試結果看,使用jemalloc作為內存管理器時內存使用量激增,需要進一步分析原因。內存使用量是否合理?是否和架構相關?問題分析
第一階段分析
1. 復現“問題”
首先需要確定該“問題”是否在x86架構上也存在,是否和操作系統或內核版本相關。為了快速驗證以上疑問,在AWS上分別創建x86實例(m5)和arm64實例(m6g)進行測試, 并沒有復現“問題”。這兩個實例默認的操作系統是Amazon Linux 2,而本地測試時使用的是CentOS8,在m6g上安裝CentOS8重新測試,“問題”復現。測試結果如下:2. 對比分析
對比測試環境,分析它們不同點,我們發現該“問題”只有在內核page size是64KB時才會出現。另外,根據前文介紹,jemalloc中extent會基于page size分配內存。而且,深入分析jemalloc代碼后還發現有多個數據結構的內存分配都涉及到page size,比如size_class, bin, extents, arena等等。頁表在操作系統中作為最基礎的內存分配結構,ARM64支持4K、16K、64K不同大小的頁表,x86只支持4KB。而本地測試使用的ARM64 CentOS的默認page size就是64KB,所以初步判斷該“問題”和page size的配置相關。3. 解決方法
即然ARM64架構支持多種page size,而page size為4KB時沒有出現問題,那么可以修改ARM64 CentOS8的內核默認的page size來解決該"問題"。修改page size方法
由于內核當前頁表大小只支持靜態配置,不支持動態修改,所以需要重新編譯內核。修改方法如下:-
在https://www.kernel.org/獲取需要的內核版本
-
解壓并修改內核配置參數
# 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并保存配置
-
編譯并安裝新的內核
# make -j # make modules_install # make install
-
重啟進入新的內核,參看page size是否修改成功
# getconf PAGE_SIZE 4096
4. 驗證
修改page size為4KB后重新測試,jemalloc內存使用量和glibc接近。測試結果如下:5. 潛在問題
至此該“問題”似乎可以通過修改page size來解決。但是,如果用戶仍然需要使用64KB的頁表,該方法將不再適用。實際上,jemalloc本身支持編譯參數“--with-lg-page=16”,該參數可以使jemalloc在page size為4KB時復用多個頁面來達到使用64KB頁面的效果。嘗試在4KB page size的系統下加入該編譯參數,并沒有出現內存使用量激增的現象。這說明除了page size,還有其他因素影響了jemalloc的內存分配,仍然需要進一步分析。第二階段分析
1. micro-benchmark
通過以上測試發現該“問題”和MySQL并沒有直接關系。為了簡化分析和復現過程,單獨開發了一個micro-benchmarkhttps://github.com/machuang1983/jemalloc_micro_benchmark該程序用于建立多個線程,每個線程分配一定內存,程序運行過程中實時打印進程的內存使用情況。通過micro-benchmark可以快速復現問題。測試結果顯示,每新建一個線程就會消耗1GB左右的內存。測試結果如下: ?再次簡化測試,直接運行單線程程序,如sleep 100,進程就會占用1GB內存。 ?由此看見,jemalloc針對一個線程進行內存初始化分配時就會分配1GB內存。需要深入分析jemalloc具體的分配機制。2. 深入分析jemalloc代碼
按前文所述,jemalloc的內存分配涉及到多個數據結構,我們結合gdb單步執行來分析jemalloc代碼,同時實時查看內存占用的變化,由此定位到關鍵代碼。調試過程中發現,base會基于默認的hugepage size分配內存,分配之后監控到內存使用量突然增大,具體代碼在https://github.com/jemalloc/jemalloc/blob/dev/src/base.c#L46-L49 ?繼續搜索hugepage size相關代碼,還發現另一處使用它來分配內存,代碼在https://github.com/jemalloc/jemalloc/blob/master/src/arena.c#L2052 ?由此可見除了page size,hugepage size對jemalloc的內存分配也有影響。通常hugepage size比page size大得多,所以hugepage size的影響會更大。3. hugepage
內存管理采用"分頁機制",但是當運行內存需求量較大時,默認page大小的頁面會導致較多的TLB miss和缺頁中斷,從而大大影響應用程序性能。所以,有些場景希望可以使用更大的內存頁作為映射單位,因此引入了hugepage。不同架構支持的hugepage size不同,見下表:4. 解決方法
ARM64 CentOS在page size=64KB時,默認hugepage size是512MB,jemalloc的base會以512MB來分配內存,而當page size=4KB時,默認hugepage size是2MB。所以回顧前面的測試,修改page size后問題消失的主要原因是默認的hugepage size改變導致的。默認hugepage size修改方法
- 修改啟動參數“default_hugepagesz=2M” ARM64支持多種hugepage size,可以使用hugepagesz啟動參數進行調整,無需重新編譯內核。
- 內核啟動時,輸入"e"進入修改啟動選項界面,加入參數“default_hugepagesz=2M”,然后輸入"ctrl+x"啟動內核。
- 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”
- 永久修改
- 臨時修改
- jemalloc編譯參數"--with-lg-hugepage=21" jemalloc支持編譯參數"--with-lg-hugepage=21",替代系統的默認的hugepage size為2MB。建議使用該方法。
5. 驗證
修改默認hugepage size后測試結果(sysbench使用256線程壓測)如下: ?測試結果顯示,將hugepage size改為2MB以后,jemalloc的內存使用情況和glibc接近。總結
該"問題"和架構無關,jemalloc作為內存管理器,如果默認hugepage size較大,會導致軟件占用較大的內存,jemalloc提供了編譯參數"--with-lg-hugepage=21"來降低這個影響。由于ARM64支持更多類型的page size和hugepage size,用以提升軟件的性能。所以用戶在ARM64系統上使用jemalloc時,需要關注默認的page size和hugepage size,并根據具體需求做出相應的調整。審核編輯 :李倩
-
管理器
+關注
關注
0文章
246瀏覽量
18530 -
MySQL
+關注
關注
1文章
816瀏覽量
26606 -
線程
+關注
關注
0文章
505瀏覽量
19700 -
malloc
+關注
關注
0文章
52瀏覽量
73
原文標題:技術分享 | Arm64 CentOS系統下MySQL使用jemalloc時的問題和解決方法
文章出處:【微信號:Ithingedu,微信公眾號:安芯教育科技】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論