十年後數據庫還是不敢擁抱NUMA?

十年後數據庫還是不敢擁抱NUMA?

在2010年前後MySQL、PG、Oracle數據庫在使用NUMA的時候碰到了性能問題,流傳最廣的這篇 MySQL – The MySQL “swap insanity” problem and the effects of the NUMA architecture 描述了性能問題的原因(文章中把原因找錯了)以及解決方案:關閉NUMA。 實際這個原因是kernel實現的一個低級bug,這個Bug在2014年修復了,但是修復這麼多年後仍然以訛傳訛,這篇文章希望正本清源、扭轉錯誤的認識。

背景

最近在做一次性能測試的時候發現MySQL實例有一個奇怪現象,在128core的物理機上運行三個MySQL實例,每個實例分別綁定32個物理core,綁定順序就是第一個0-31、第二個32-63、第三個64-95,實際運行結果讓人大跌眼鏡,如下圖

undefined

從CPU消耗來看差異巨大,高的實例CPU用到了2500%,低的才488%,差了5倍。但是神奇的是他們的QPS一樣,執行的SQL也是一樣

undefined
所有MySQL實例流量一樣

那麼問題來了爲什麼在同樣的機器上、同樣的流量下CPU使用率差了這麼多? 換句話來問就是CPU使用率高就有效率嗎?

這臺物理機CPU 信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#lscpu
Architecture: aarch64
Byte Order: Little Endian
CPU(s): 128
On-line CPU(s) list: 0-127
Thread(s) per core: 1
Core(s) per socket: 64
Socket(s): 2
NUMA node(s): 1
Model: 3
BogoMIPS: 100.00
L1d cache: 32K
L1i cache: 32K
L2 cache: 2048K
L3 cache: 65536K
NUMA node0 CPU(s): 0-127
Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid

 

原因分析

先來看這兩個MySQL 進程的Perf數據

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#第二個 MySQL IPC只有第三個的30%多點,這就是爲什麼CPU高這麼多,但是QPS差不多
perf stat -e branch-misses,bus-cycles,cache-misses,cache-references,cpu-cycles,instructions,L1-dcache-load-misses,L1-dcache-loads,L1-dcache-store-misses,L1-dcache-stores,L1-icache-load-misses,L1-icache-loads,branch-load-misses,branch-loads,dTLB-load-misses,iTLB-load-misses -a -p 61238
^C
Performance counter stats for process id '61238':
 
86,491,052 branch-misses (58.55%)
98,481,418,793 bus-cycles (55.64%)
113,095,618 cache-misses # 6.169 % of all cache refs (53.20%)
1,833,344,484 cache-references (52.00%)
101,516,165,898 cpu-cycles (57.09%)
4,229,190,014 instructions # 0.04 insns per cycle (55.91%)
111,780,025 L1-dcache-load-misses # 6.34% of all L1-dcache hits (55.40%)
1,764,421,570 L1-dcache-loads (52.62%)
112,261,128 L1-dcache-store-misses (49.34%)
1,814,998,338 L1-dcache-stores (48.51%)
219,372,119 L1-icache-load-misses (49.56%)
2,816,279,627 L1-icache-loads (49.15%)
85,321,093 branch-load-misses (50.38%)
1,038,572,653 branch-loads (50.65%)
45,166,831 dTLB-load-misses (51.98%)
29,892,473 iTLB-load-misses (52.56%)
 
1.163750756 seconds time elapsed
 
#第三個 MySQL
perf stat -e branch-misses,bus-cycles,cache-misses,cache-references,cpu-cycles,instructions,L1-dcache-load-misses,L1-dcache-loads,L1-dcache-store-misses,L1-dcache-stores,L1-icache-load-misses,L1-icache-loads,branch-load-misses,branch-loads,dTLB-load-misses,iTLB-load-misses -a -p 53400
^C
Performance counter stats for process id '53400':
 
295,575,513 branch-misses (40.51%)
110,934,600,206 bus-cycles (39.30%)
537,938,496 cache-misses # 8.310 % of all cache refs (38.99%)
6,473,688,885 cache-references (39.80%)
110,540,950,757 cpu-cycles (46.10%)
14,766,013,708 instructions # 0.14 insns per cycle (46.85%)
538,521,226 L1-dcache-load-misses # 8.36% of all L1-dcache hits (48.00%)
6,440,728,959 L1-dcache-loads (46.69%)
533,693,357 L1-dcache-store-misses (45.91%)
6,413,111,024 L1-dcache-stores (44.92%)
673,725,952 L1-icache-load-misses (42.76%)
9,216,663,639 L1-icache-loads (38.27%)
299,202,001 branch-load-misses (37.62%)
3,285,957,082 branch-loads (36.10%)
149,348,740 dTLB-load-misses (35.20%)
102,444,469 iTLB-load-misses (34.78%)
 
8.080841166 seconds time elapsed

從上面可以看到 IPC 差異巨大0.04 VS 0.14 ,也就是第一個MySQL的CPU效率很低,我們看到的CPU running實際是CPU在等待(stall)。

CPU的實際信息

找到同一個機型,但是NUMA開着的查了一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#lscpu
Architecture: aarch64
Byte Order: Little Endian
CPU(s): 128
On-line CPU(s) list: 0-127
Thread(s) per core: 1
Core(s) per socket: 64
Socket(s): 2
NUMA node(s): 16
Model: 3
BogoMIPS: 100.00
L1d cache: 32K
L1i cache: 32K
L2 cache: 2048K
L3 cache: 65536K
NUMA node0 CPU(s): 0-7
NUMA node1 CPU(s): 8-15
NUMA node2 CPU(s): 16-23
NUMA node3 CPU(s): 24-31
NUMA node4 CPU(s): 32-39
NUMA node5 CPU(s): 40-47
NUMA node6 CPU(s): 48-55
NUMA node7 CPU(s): 56-63
NUMA node8 CPU(s): 64-71
NUMA node9 CPU(s): 72-79
NUMA node10 CPU(s): 80-87
NUMA node11 CPU(s): 88-95
NUMA node12 CPU(s): 96-103
NUMA node13 CPU(s): 104-111
NUMA node14 CPU(s): 112-119
NUMA node15 CPU(s): 120-127
Flags: fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid

這告訴我們實際上這個機器有16個NUMA,跨NUMA訪問內存肯定比訪問本NUMA內的要慢幾倍。

關於NUMA

如下圖,是一個Intel Xeon E5 CPU的架構信息,左右兩邊的大紅框分別是兩個NUMA,每個NUMA的core訪問直接插在自己紅環上的內存必然很快,如果訪問插在其它NUMA上的內存還要走兩個紅環之間上下的黑色箭頭線路,所以要慢很多。

img

實際測試Intel的E5-2682(對應V42機型)和8269(對應V62機型) 的CPU跨Socket(這兩塊CPU內部不再是上圖的紅環Bus,而是改用了Mesh Bus一個Die就是一個NUMA,服務器有兩路,也就是一個Socket就是一個NUMA),也就是跨NUMA訪問內存的延遲是本Node延遲的將近2倍。測試工具從這裏下載

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//E5-2682
Intel(R) Memory Latency Checker - v3.9
Measuring idle latencies (in ns)...
Numa node
Numa node 0 1
0 85.0 136.3
1 137.2 84.2
 
//8269
Intel(R) Memory Latency Checker - v3.9
Measuring idle latencies (in ns)...
Numa node
Numa node 0 1
0 78.6 144.1
1 144.7 78.5

開啓NUMA會優先就近使用內存,在本NUMA上的內存不夠的時候可以選擇回收本地的PageCache還是到其它NUMA 上分配內存,這是可以通過Linux參數 zone_reclaim_mode 來配置的,默認是到其它NUMA上分配內存,也就是跟關閉NUMA是一樣的。

這個架構距離是物理上就存在的不是你在BIOS裏關閉了NUMA差異就消除了,我更願意認爲在BIOS裏關掉NUMA只是掩耳盜鈴。

以上理論告訴我們:也就是在開啓NUMA和 zone_reclaim_mode 默認在內存不夠的如果去其它NUMA上分配內存,比關閉NUMA要快很多而沒有任何害處。

UMA和NUMA對比

The SMP/UMA architecture

img

The NUMA architecture

img

Modern multiprocessor systems mix these basic architectures as seen in the following diagram:

img

In this complex hierarchical scheme, processors are grouped by their physical location on one or the other multi-core CPU package or “node.” Processors within a node share access to memory modules as per the UMA shared memory architecture. At the same time, they may also access memory from the remote node using a shared interconnect, but with slower performance as per the NUMA shared memory architecture.

03-05-Broadwell_HCC_Architecture

對比測試Intel NUMA 性能

對如下Intel CPU進行一些測試,在開啓NUMA的情況下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 64
On-line CPU(s) list: 0-63
Thread(s) per core: 2
Core(s) per socket: 16
Socket(s): 2
NUMA node(s): 2
Vendor ID: GenuineIntel
CPU family: 6
Model: 79
Model name: Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz
Stepping: 1
CPU MHz: 2500.000
CPU max MHz: 3000.0000
CPU min MHz: 1200.0000
BogoMIPS: 5000.06
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 40960K
NUMA node0 CPU(s): 0-15,32-47
NUMA node1 CPU(s): 16-31,48-63
Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 ds_cpl vmx smx est tm2 ssse3 fma cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm abm 3dnowprefetch ida arat epb invpcid_single pln pts dtherm spec_ctrl ibpb_support tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm cqm rdt rdseed adx smap xsaveopt cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local cat_l3
 
#numastat
node0 node1
numa_hit 129600200 60501102
numa_miss 0 0
numa_foreign 0 0
interleave_hit 108648 108429
local_node 129576548 60395061
other_node 23652 106041

我在這個64core的物理機上運行一個MySQL 實例,先將MySQL進程綁定在0-63core,0-31core,以及0-15,32-47上

用sysbench對一億條記錄跑點查,數據都加載到內存中了:

  • 綁0-63core qps 不到8萬,總cpu跑到5000%,降低併發的話qps能到11萬;
  • 如果綁0-31core qps 12萬,總cpu跑到3200%,IPC 0.29;
  • 如果綁同一個numa下的32core,qps飆到27萬,總CPU跑到3200% IPC: 0.42;
  • 綁0-15個物理core,qps能到17萬,綁32-47也是一樣的效果;

undefined

從這個數據看起來即使Intel在只有兩個NUMA的情況下跨性能差異也有2倍,可見正確的綁核方法收益巨大,尤其是在刷榜的情況下, NUMA更多性能差異應該會更大。

說明前面的理論是正確的。

來看看不通綁核情況下node之間的帶寬利用情況:

image-20210525151537507

image-20210525151622425

實際在不開NUMA的同樣CPU上,進行以上各種綁核測試,測試結果也完全一樣。

如果比較讀寫混合場景的話肯定會因爲寫鎖導致CPU跑起來,最終的性能差異也不會這麼大,但是綁在同一個NUMA下的性能肯定要好,IPC也會高一些。具體好多少取決於鎖的競爭程度。

爲什麼集團內外所有物理機都把NUMA關掉了呢?

10年前幾乎所有的運維都會多多少少被NUMA坑害過,讓我們看看究竟有多少種在NUMA上栽的方式:

最有名的是這篇 MySQL – The MySQL “swap insanity” problem and the effects of the NUMA architecture

我總結下這篇2010年的文章說的是啥:

  • 如果本NUMA內存不夠的時候,Linux會優先回收PageCache內存,即使其它NUMA還有內存
  • 回收PageCache經常會造成系統卡頓,這個卡頓不能接受

所以文章給出的解決方案就是(三選一):

  • 關掉NUMA
  • 或者啓動MySQL的時候指定不分NUMA,比如:/usr/bin/numactl –interleave all $cmd
  • 或者啓動MySQL的時候先回收所有PageCache

我想這就是這麼多人在上面栽了跟頭,所以乾脆一不做二不休乾脆關了NUMA 一了百了。

但真的NUMA有這麼糟糕?或者說Linux Kernel有這麼笨,默認優先去回收PageCache嗎?

Linux Kernel對NUMA內存的使用

實際我們使用NUMA的時候期望是:優先使用本NUMA上的內存,如果本NUMA不夠了不要優先回收PageCache而是優先使用其它NUMA上的內存。

zone_reclaim_mode

事實上Linux識別到NUMA架構後,默認的內存分配方案就是:優先嚐試在請求線程當前所處的CPU的Local內存上分配空間。如果local內存不足,優先淘汰local內存中無用的Page(Inactive,Unmapped)。然後纔到其它NUMA上分配內存。

intel 芯片跨node延遲遠低於其他家,所以跨node性能損耗不大

zone_reclaim_mode,它用來管理當一個內存區域(zone)內部的內存耗盡時,是從其內部進行內存回收還是可以從其他zone進行回收的選項:

zone_reclaim_mode:

Zone_reclaim_mode allows someone to set more or less aggressive approaches to
reclaim memory when a zone runs out of memory. If it is set to zero then no
zone reclaim occurs. Allocations will be satisfied from other zones / nodes
in the system.

zone_reclaim_mode的四個參數值的意義分別是:

0 = Allocate from all nodes before reclaiming memory
1 = Reclaim memory from local node vs allocating from next node
2 = Zone reclaim writes dirty pages out
4 = Zone reclaim swaps pages

1
2
# cat /proc/sys/vm/zone_reclaim_mode
0

我查了2.6.32以及4.19.91內核的機器 zone_reclaim_mode 都是默認0 ,也就是kernel會:優先使用本NUMA上的內存,如果本NUMA不夠了不要優先回收PageCache而是優先使用其它NUMA上的內存。這也是我們想要的

Kernel文檔也告訴大家默認就是0,但是爲什麼會出現優先回收了PageCache呢?

查看kernel提交記錄

github kernel commit

undefined

undefined

undefined

關鍵是上圖紅框中的代碼,node distance比較大(也就是開啓了NUMA的話),強制將 zone_reclaim_mode設爲1,這是2014年提交的代碼,將這個強制設爲1的邏輯去掉了。

這也就是爲什麼之前大佬們碰到NUMA問題後嘗試修改 zone_reclaim_mode 沒有效果,也就是2014年前只要開啓了NUMA就強制線回收PageCache,即使設置zone_reclaim_mode也沒有意義,真是個可怕的Bug。

驗證一下zone_reclaim_mode 0是生效的

內核版本:3.10.0-327.ali2017.alios7.x86_64

測試方法

先將一個160G的文件加載到內存裏,然後再用代碼分配64G的內存出來使用。
單個NUMA node的內存爲256G,本身用掉了60G,加上這次的160G的PageCache,和之前的一些其他PageCache,總的 PageCache用了179G,那麼這個node總內存還剩256G-60G-179G,

如果這個時候再分配64G內存的話,本node肯定不夠了,我們來看在 zone_reclaim_mode=0 的時候是優先回收PageCache還是分配了到另外一個NUMA node(這個NUMA node 有240G以上的內存空閒)

測試過程

分配64G內存

1
2
3
#taskset -c 0 ./alloc 64
To allocate 64GB memory
Used time: 39 seconds

undefined

從如上截圖來看,再分配64G內存的時候即使node0不夠了也沒有回收node0上的PageCache,而是將內存跨NUMA分配到了node1上,符合預期!

釋放這64G內存後,如下圖可以看到node0回收了25G,剩下的39G都是在node1上:
undefined

將 /proc/sys/vm/zone_reclaim_mode 改成 1 繼續同樣的測試

可以看到zone_reclaim_mode 改成 1,node0內存不夠了也沒有分配node1上的內存,而是從PageCache回收了40G內存,整個分配64G內存的過程也比不回收PageCache慢了12秒,這12秒就是額外的卡頓

undefined

測試結論:從這個測試可以看到NUMA 在內存使用上不會優先回收 PageCache 了

innodb_numa_interleave

從5.7開始,mysql增加了對NUMA的無感知:innodb_numa_interleave,也就是在開了NUMA的機器上,使用內錯交錯來分配內存,相當於使用上關掉 NUMA

For the innodb_numa_interleave option to be available, MySQL must be compiled on a NUMA-enabled Linux system.

當開啓了 innodb_numa_interleave 的話在爲innodb buffer pool分配內存的時候將 NUMA memory policy 設置爲 MPOL_INTERLEAVE 分配完後再設置回 MPOL_DEFAULT(OS默認內存分配行爲,也就是zone_reclaim_mode指定的行爲)。

innodb_numa_interleave參數是爲innodb更精細化地分配innodb buffer pool 而增加的。很典型地innodb_numa_interleave爲on只是更好地規避了前面所說的zone_reclaim_mode的kernel bug,修復後這個參數沒有意義了

AUTOMATIC NUMA BALANCING

RedHat 7默認會自動讓內存或者進程就近遷移,讓內存和CPU距離更近以達到最好的效果

Automatic NUMA balancing improves the performance of applications running on NUMA hardware systems. It is enabled by default on Red Hat Enterprise Linux 7 systems.

An application will generally perform best when the threads of its processes are accessing memory on the same NUMA node as the threads are scheduled. Automatic NUMA balancing moves tasks (which can be threads or processes) closer to the memory they are accessing. It also moves application data to memory closer to the tasks that reference it. This is all done automatically by the kernel when automatic NUMA balancing is active.

對應參數

1
cat /proc/sys/kernel/numa_balancing shows 1

監控

查找相應的內存和調度器事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#perf stat -e sched:sched_stick_numa,sched:sched_move_numa,sched:sched_swap_numa,migrate:mm_migrate_pages,minor-faults -p 7191
Performance counter stats for process id '7191':
 
0 sched:sched_stick_numa (100.00%)
1 sched:sched_move_numa (100.00%)
0 sched:sched_swap_numa
0 migrate:mm_migrate_pages
286 minor-faults
 
# perf stat -e sched:sched_stick_numa,sched:sched_move_numa,sched:sched_swap_numa,migrate:mm_migrate_pages,minor-faults -p PID
...
1 sched:sched_stick_numa
3 sched:sched_move_numa
41 sched:sched_swap_numa
5,239 migrate:mm_migrate_pages
50,161 minor-faults
 
#perf stat -e sched:sched_stick_numa,sched:sched_move_numa,sched:sched_swap_numa,migrate:mm_migrate_pages,minor-faults -p 676322
Performance counter stats for process id '676322':
 
0 sched:sched_stick_numa
16 sched:sched_move_numa
0 sched:sched_swap_numa
24 migrate:mm_migrate_pages
2,079 minor-faults

總結

  • 放棄對NUMA的偏見吧,優先回收 PageCache 這個Bug早已修復了
  • 按NUMA綁定core收益巨大,即使只有兩個NUMA的intel芯片,也有一倍以上的性能提升,在飛騰等其他芯片上收益更大
  • 沒必要自欺欺人關掉NUMA了
  • RDS這樣獨佔物理機的服務可以做到按NUMA來綁定core,收益可觀
  • ECS售賣如果能夠精確地按NUMA綁核的話性能,超賣比能高很多
  • 在刷tpcc數據的時候更應該開NUMA和正確綁核

我個人一直對集團所有機器默認關閉NUMA耿耿於懷,因爲定製的物理機(BIOS也是定製的)BIOS默認就是關閉NUMA的,裝機還得一臺臺手工打開(跪了,幾十萬臺啊),算是理清了來龍去脈。因爲一個kernel的bug讓大家對NUMA一直有偏見,即使14年已經修復了,大家還是以訛傳訛,沒必要。

關於cpu爲什麼高但是沒有產出的原因是因爲CPU流水線長期stall,導致很低的IPC,所以性能自然上不去,可以看這篇文章

其他同學測試的結論:

  • Hadoop離線作業在 Intel(R) Xeon(R) Platinum 8163 CPU @ 2.50GHz 24 cores/socket * 2, Turbo Off 下打開NUMA後性能提升8%

一些其它不好解釋的現象:

  1. 增加少量跨NUMA 的core進來時能增加QPS的,但是隨着跨NUMA core越來越多(總core也越來越多)QPS反而會達到一個峯值後下降—效率低的core多了,搶走任務,執行得慢
  2. 壓12-19和8-15同樣8core,不跨NUMA的8-15性能只好5%左右(87873 VS 92801) — 難以解釋
  3. 由1、2所知在測試少量core的時候跨NUMA性能下降體現不出來
  4. 在壓0-31core的時候,如果運行 perf這個時候QPS反而會增加(13萬上升到15萬)— 搶走了一些CPU資源,讓某個地方競爭反而減小了
  5. 綜上在我個人理解是core越多的時候UPI壓力到了瓶頸,纔會出現加core性能反而下降

系列文章

CPU的製造和概念

CPU 性能和Cache Line

Perf IPC以及CPU性能

Intel、海光、鯤鵬920、飛騰2500 CPU性能對比

飛騰ARM芯片(FT2500)的性能測試的性能測試/)

十年後數據庫還是不敢擁抱NUMA?

一次海光物理機資源競爭壓測的記錄

Intel PAUSE指令變化是如何影響自旋鎖以及MySQL的性能的

參考資料

https://www.redhat.com/files/summit/session-assets/2018/Performance-analysis-and-tuning-of-Red-Hat-Enterprise-Linux-Part-1.pdf

https://informixdba.wordpress.com/2015/10/16/zone-reclaim-mode/

https://queue.acm.org/detail.cfm?id=2513149

NUMA DEEP DIVE PART 1: FROM UMA TO NUMA 這是一個系列,都很乾貨,值得推薦

https://15721.courses.cs.cmu.edu/spring2016/papers/p743-leis.pdf Morsel-Driven Parallelism: A NUMA-Aware Query Evaluation Framework for the Many-Core Age 論文給出了很多numa-aware下的bandwidth、latency數據,以及對THC-H的影響

 

作者|plantegg

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章