NUMA导致的MySQL服务器SWAP问题分析
【作者】
王棟:攜程技術(shù)保障中心數(shù)據(jù)庫專家,對數(shù)據(jù)庫疑難問題的排查和數(shù)據(jù)庫自動(dòng)化智能化運(yùn)維工具的開發(fā)有強(qiáng)烈的興趣。
【問題描述】
我們知道當(dāng)mysqld進(jìn)程使用到SWAP時(shí),就會(huì)嚴(yán)重影響到MySQL的性能。SWAP的問題比較復(fù)雜,本文會(huì)從SWAP的原理開始,分享我們碰到的案例和分析思路。
【SWAP原理】
swap是把一部分磁盤空間或文件,當(dāng)作內(nèi)存來使用。它有換出和換入兩種方式,換出是進(jìn)程把不活躍的內(nèi)存數(shù)據(jù)存儲(chǔ)到磁盤上,并釋放數(shù)據(jù)占用的內(nèi)存空間,換入是進(jìn)程再次訪問這部分?jǐn)?shù)據(jù)的時(shí)候,從磁盤讀到內(nèi)存中。
swap擴(kuò)展了內(nèi)存空間,是為了回收內(nèi)存。內(nèi)存回收的機(jī)制,一種是當(dāng)內(nèi)存分配沒有足夠的空間時(shí),系統(tǒng)需要回收一部分內(nèi)存,稱為直接內(nèi)存回收。另外還有一個(gè)專門的kswapd0進(jìn)程用來定期回收內(nèi)存。為了衡量內(nèi)存的使用情況,定義了三個(gè)內(nèi)存閥值,分為頁最小水位(min)、頁低水位(low)、頁高水位(high)
執(zhí)行下面命令,可以看到水位線對應(yīng)的值,如下圖所示
內(nèi)存回收行為主要有
1、當(dāng)系統(tǒng)剩余內(nèi)存低于low時(shí),kswapd開始起作用進(jìn)行內(nèi)存回收,直到內(nèi)存達(dá)到high水位。
2、當(dāng)剩余內(nèi)存達(dá)到min時(shí)就會(huì)觸發(fā)直接回收。
3、當(dāng)觸發(fā)全局回收,并且file+free<=high時(shí),一定會(huì)進(jìn)行針對匿名頁的swap。
【NUMA與SWAP】
有些案例我們發(fā)現(xiàn)系統(tǒng)還有大量剩余空間的情況下,已經(jīng)使用了swap。這正是NUMA架構(gòu)導(dǎo)致的。NUMA架構(gòu)下每個(gè)Node都有本地的內(nèi)存空間,Node間內(nèi)存使用不均衡,當(dāng)某個(gè)Node的內(nèi)存不足時(shí),就可能導(dǎo)致swap的產(chǎn)生。
【swappiness】
我們大概理解了內(nèi)存回收的機(jī)制,回收的內(nèi)存包括文件頁和匿名頁。對文件頁的回收就是直接回收緩存,或者把臟頁寫回到磁盤再進(jìn)行回收。對匿名頁的回收,就是通過swap,將數(shù)據(jù)寫入磁盤后再釋放內(nèi)存。
通過調(diào)整/proc/sys/vm/swappiness的值,可以調(diào)整使用swap的積極程度,swappiness值從0-100,值越小,傾向于回收文件頁,盡量少的使用swap。我們最初將這個(gè)值調(diào)整為1,但發(fā)現(xiàn)并不能避免swap的產(chǎn)生。實(shí)際上即使將這個(gè)值設(shè)置0,當(dāng)滿足file+free<=high時(shí),還是會(huì)發(fā)生swap。
【關(guān)閉NUMA的方案】
在NUMA開啟的情況,由于NUMA節(jié)點(diǎn)間內(nèi)存使用不均衡,可能導(dǎo)致swap,解決這個(gè)問題主要有下面一些方案
2、 Linux Kernel啟動(dòng)參數(shù)中加上numa=off,需要重啟服務(wù)器
3、 在BIOS層面關(guān)閉NUMA
4、 MySQL 5.6.27/5.7.9開始引用innodb_numa_interleave選項(xiàng)
對于2、3、4關(guān)閉NUMA的方案比較簡單,不做詳細(xì)描述,下面重點(diǎn)描述下方案1
【開啟numa interleave訪問的步驟】
1、 yum install numactl -y 2、修改/usr/bin/mysqld_safe文件cmd="`mysqld_ld_preload_text`$NOHUP_NICENESS"下新增一條腳本cmd="/usr/bin/numactl --interleave all $cmd" 3、service mysql stop 4、寫入硬盤,防止數(shù)據(jù)丟失sync;sync;sync 5、延遲10秒sleep 10 6、清理pagecache、dentries和inodessysctl -q -w vm.drop_caches=3 7、service mysql start 8、驗(yàn)證numactl –interleave all是否生效,可以通過下面命令,interleave_hit是采用interleave策略從該節(jié)點(diǎn)分配的次數(shù),沒有啟動(dòng)interleave策略的服務(wù)器,這個(gè)值會(huì)很低numastat -mn -p `pidof mysqld`至此我們MySQL5.6的服務(wù)器通過上面方案解決了由于NUMA Node間內(nèi)存分配不均導(dǎo)致的swap的問題。對于MySQL5.7.23版本的服務(wù)器,我們使用了innodb_numa_interleave選項(xiàng),但問題并沒有徹底解決。
【使用MySQL5.7新增innodb_numa_interleave選項(xiàng)的問題】
在開啟innodb_numa_interleave選項(xiàng)的服務(wù)器中,仍然會(huì)存在NUMA Node間內(nèi)存分配不均衡的問題,會(huì)導(dǎo)致swap產(chǎn)生。針對這個(gè)問題做了進(jìn)一步分析:
1、 MySQL 版本為5.7.23,已經(jīng)開啟了innodb_numa_interleave
2、 使用命令查看mysqld進(jìn)程的內(nèi)存使用情況,numastat -mn `pidof mysqld`
可以看出Node 0使用了約122.5G內(nèi)存,Node 1使用了約68.2G內(nèi)存,其中Node0上的可用空間只剩566M,如果后面申請Node 0節(jié)點(diǎn)分配內(nèi)存不足,就可能產(chǎn)生swap
Node 0 Node 1 Total
--------------- --------------- ---------------
Huge 0.00 0.00 0.00
Heap 0.00 0.00 0.00
Stack 0.01 0.07 0.09
Private 125479.61 69856.82 195336.43
---------------- --------------- --------------- ---------------
Total 125479.62 69856.90 195336.52
3、是innodb_numa_interleave沒有生效嗎,通過分析/proc/1801/numa_maps文件可以進(jìn)一步查看mysqld進(jìn)程的內(nèi)存分配情況
以其中一條記錄為例,
interleave:0-1 表示內(nèi)存所用的NUMA策略,這里使用了Interleave方式
anon=5734148 匿名頁數(shù)量
dirty=5734148 臟頁數(shù)量
active=5728403 活動(dòng)列表頁面的數(shù)量
N0=3607212 N1=2126936 節(jié)點(diǎn)0、1分配的頁面數(shù)量
kernelpagesize_kB=4 頁面大小為4K
7f9067850000 interleave:0-1 anon=5734148 dirty=5734148 active=5728403 N0=3607212 N1=2126936 kernelpagesize_kB=4
4、通過解析上面文件,對Node 0和Node 1節(jié)點(diǎn)分配的頁面數(shù)量做統(tǒng)計(jì),可以計(jì)算出Node 0通過interleave方式分配了約114.4G內(nèi)存,Node 1通過interleave方式分配了約64.7G內(nèi)存
說明innodb_numa_interleave開關(guān)是實(shí)際生效的,但是即使mysql使用了interleave的分配方式,仍然存在不均衡的問題
5、通過innodb_numa_interleave相關(guān)的源碼,可以看出當(dāng)開關(guān)開啟時(shí),MySQL調(diào)用linux的set_mempolicy函數(shù)指定MPOL_INTERLEAVE策略跨節(jié)點(diǎn)來分配內(nèi)存set_mempolicy(MPOL_INTERLEAVE, numa_all_nodes_ptr->maskp, numa_all_nodes_ptr->size)
當(dāng)開關(guān)關(guān)閉時(shí),set_mempolicy(MPOL_DEFAULT, NULL, 0),使用默認(rèn)的本地分配策略
#ifdef HAVE_LIBNUMA
#include <numa.h>
#include <numaif.h>
struct set_numa_interleave_t
{
set_numa_interleave_t()
{
if (srv_numa_interleave) {
ib::info() << "Setting NUMA memory policy to"
" MPOL_INTERLEAVE";
if (set_mempolicy(MPOL_INTERLEAVE,
numa_all_nodes_ptr->maskp,
numa_all_nodes_ptr->size) != 0) {
ib::warn() << "Failed to set NUMA memory"
" policy to MPOL_INTERLEAVE: "
<< strerror(errno);
}
}
}
~set_numa_interleave_t()
{
if (srv_numa_interleave) {
ib::info() << "Setting NUMA memory policy to"
" MPOL_DEFAULT";
if (set_mempolicy(MPOL_DEFAULT, NULL, 0) != 0) {
ib::warn() << "Failed to set NUMA memory"
" policy to MPOL_DEFAULT: "
<< strerror(errno);
}
} }};
【測試對比開啟innodb_numa_interleave開關(guān)和numactl –interleave=all啟動(dòng)mysqld進(jìn)程兩種方式NUMA節(jié)點(diǎn)的內(nèi)存分配情況】
場景一、numactl --interleave=all啟動(dòng)mysqld進(jìn)程的方式
1、 修改systemd配置文件,刪除my.cnf中innodb_numa_interleave=on開關(guān)配置,重啟MySQL服務(wù)
2、 運(yùn)行select count(*) from test.sbtest1語句,這個(gè)表中有2億條記錄,運(yùn)行14分鐘,會(huì)將表中的數(shù)據(jù)讀到buffer pool中
3、運(yùn)行結(jié)束后,分析numa_maps文件可以看到mysqld進(jìn)程采用了interleave跨節(jié)點(diǎn)訪問的分配方式,兩個(gè)Node間分配的內(nèi)存大小基本一致
7f9a3c5b3000 interleave:0-1 anon=2497435 dirty=2497435 N0=1247949 N1=1249486 kernelpagesize_kB=4
4、mysqld進(jìn)程總的分配也是均衡的
場景二、開啟innodb_numa_interleave的方式
1、增加my.cnf中innodb_numa_interleave=on開關(guān)配置,重啟MySQL服務(wù),執(zhí)行與場景一相關(guān)的SQL語句
2、運(yùn)行結(jié)束后,分析numa_maps文件可以看到mysqld進(jìn)程采用interleave方式分配的在不同Node間是基本平衡的
7f74a2e14000 interleave:0-1 anon=214208 dirty=214208 N0=107104 N1=107104 kernelpagesize_kB=4
7f776ce90000 interleave:0-1 anon=218128 dirty=218128 N0=108808 N1=109320 kernelpagesize_kB=4
3、不過仍有部分內(nèi)存使用了default的本地分配策略,這部分內(nèi)存全部分配到了Node 0上
4、最終mysqld進(jìn)程分配的內(nèi)存Node 0 比Node 1大了約1G
【MySQL5.7.23啟用numactl –interleave=all的方法】
MySQL5.7版本不再使用mysqld_safe文件,所以啟用numactl –interleave=all的方式,與MySQL 5.6的方法不同,總結(jié)如下:
1、修改vim /etc/my.cnf文件,刪除innodb_numa_interleave配置項(xiàng) 2、修改systemd 的本地配置文件,vim /usr/lib/systemd/system/mysqld.service,增加/usr/bin/numactl --interleave=all命令# Start main serviceExecStart=/usr/bin/numactl --interleave=all /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid $MYSQLD_OPTS 3、停止MySQL服務(wù)systemctl stop mysqld.service 4、重新加載配置文件systemctl daemon-reload 5、寫入硬盤,防止數(shù)據(jù)丟失sync;sync;sync 6、延遲10秒 sleep 10 7、清理pagecache、dentries和inodessysctl -q -w vm.drop_caches=3 8、啟動(dòng)MySQL服務(wù)systemctl start mysqld.service 9、驗(yàn)證是否生效,首先確認(rèn)show global variables like ' innodb_numa_interleave';開關(guān)為關(guān)閉狀態(tài)正常情況下mysqld進(jìn)程會(huì)全部采用interleave跨節(jié)點(diǎn)訪問的分配方式,如果可以查詢到其他訪問方式的信息,表示interleave方式?jīng)]有正常生效less /proc/`pidof mysqld`/numa_maps|grep -v 'interleave'【結(jié)論】
numactl –interleave=all啟動(dòng)mysqld進(jìn)程的方式NUMA不同Node間分配的內(nèi)存會(huì)更加均衡。
這個(gè)差異是與innodb_numa_interleave參數(shù)執(zhí)行的策略有關(guān),開啟后,全局內(nèi)存采用了interleave的分配方式,但線程內(nèi)存采用了default的本地分配方式。
而如果使用numactl –interleave=all啟動(dòng)mysqld進(jìn)程,所有內(nèi)存都會(huì)采用interleave的分配方式。
轉(zhuǎn)載于:https://www.cnblogs.com/CtripDBA/p/11541680.html
總結(jié)
以上是生活随笔為你收集整理的NUMA导致的MySQL服务器SWAP问题分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为什么一直没有意识到自己还是面向过程编程
- 下一篇: 泛型应用