資料庫核心月報-2015/07-MySQL·社群動態·MySQL記憶體分配支援NUMA

db匠發表於2016-05-23

NUMA 問題曾經一直是困擾DBA的一個大問題,早在 2010 年, 就有人給MySQL報了Bug#57241, 指出了MySQL在x86系統下存在嚴重的 “swap insanity” 問題。在NUMA架構越來越普遍的今天,這個問題越來越嚴重。

MySQL的 swap insanity 問題

有同學專門翻譯了Jeremy Cole關於 “swap insanity” 問題的文章,原文看這裡

如果你沒空看的話,這裡簡單描述一下,就是當你把主機大部分記憶體分配給InnoDB時,你會發現明明作業系統還有很多記憶體,但是卻有很多記憶體被交換到了SWAP分割槽。

這裡可以下載到一個測試的C程式碼,如果你有NUMA架構的伺服器,可以測試下不同分配方式的效能差異:

sudo -s
echo 2048 > /proc/sys/vm/nr_hugepages
echo 1000000000000 > /proc/sys/kernel/shmmax

# Node local allocation
for i in `seq 0 4 127`
do
./latency2001 -a $i -c $i -l 128M
done

# Allocate on memory on CPU 0
for i in `seq 0 4 127`
do
./latency2001 -a 0 -c $i -l 128M
done

有兩個方式可以解決這個問題:
1. 在Linux Kernel啟動引數中加上numa=off(這樣也會影響到其他程式使用NUMA);
2. 在mysqld_safe指令碼中加上“numactl –interleave all”來啟動mysqld。

當然如果跑多例項,我也用過直接繫結mysqld程式到某個numa節點的方式,不過這要求每個例項分配的記憶體不超過每個NUMA節點管理的記憶體。指令碼可以看這裡

5年過去了,官方依然沒有解決這個Bug。但好訊息是,官方終於著手解決這個問題了,Stewart Smith 同學提交的Bug#72811,其Patch即將出現在MySQL 5.6.27, 5.7.9 版本之中。

程式碼層面解決NUMA問題

如果在程式碼層面徹底解決NUMA問題,那麼我們需要解決兩個問題:
1. 全域性記憶體應該採用interleave的分配方式分散在不同的numa node上;
2. 執行緒記憶體應該採用local的分配方式分配線上程執行的numa node上。

Linux 提供了 set_mempolicy() 函式可以用來設定程式的記憶體分配策略,其中預設的MPOL_DEFAULT策略就是在當前執行的節點上分配記憶體,而MPOL_INTERLEAVE策略則是跨所有節點來分配記憶體。這個函式的說明可以看這裡

因此對於MySQL Server和InnoDB引擎都需要做修改:
1. 在mysqld_main()入口設定 set_mempolicy(MPOL_INTERLEAVE, NULL, 0) 啟用全域性分配方式;
2. 在MySQL啟動完成之後設定set_mempolicy(MPOL_DEFAULT, NULL, 0) 啟用本地分配方式;
3. 在InnoDB入口時設定 set_mempolicy(MPOL_INTERLEAVE, NULL, 0) 啟用全域性分配方式;
4. 在Buffer Pool分配完成時設定 set_mempolicy(MPOL_DEFAULT, NULL, 0) 啟用本地分配方式。

MySQL 5.6.27, 5.7.9 釋出之後,將會增加一個 innodb_numa_interleave 引數來控制這個策略。innodb_numa_interleave 如果開啟,那麼將會按上面的策略來設定記憶體分配方式,如果關閉或者主機不支援NUMA,那麼還是按原來的方式分配。

我們一起期待新版本的釋出吧,媽媽再也不用擔心我的NUMA了!


相關文章