1. 前言
UMA(Non-Uniform Memory Access)是一种计算机体系结构设计,旨在提高多处理器系统中的内存访问效率。在传统的对称多处理器(SMP)系统中,所有处理器共享同一总线和内存,每个处理器可以直接访问所有内存位置,但访问延迟可能会因为总线争用而增加。
而在 NUMA 架构中,处理器和内存被组织成多个节点,每个节点包含一组处理器和与之关联的一部分内存。每个节点都有本地内存,可以直接访问,而对于其他节点的内存,需要通过跨节点的连接进行访问。这种设计使得每个节点内的访问延迟较低,但跨节点访问的延迟较高。
NUMA 的设计初衷是解决多处理器系统中的内存访问瓶颈问题。通过将处理器和内存划分为多个节点,可以减少内存访问的竞争,提高整体性能和扩展性。NUMA 架构适用于需要处理大量数据的高性能计算、大规模数据库和虚拟化等应用场景。
2. 三种常见服务器架构
SMP(Symmetric Multi-Processor,对称多处理结构)。单主机架构。
NUMA(Non-Uniform Memory Access,非一致性内存访问)。单主机架构。
MPP(Massive Parallel Processing,海量并行处理结构)。多主机架构。
SMP(Symmetric Multi-Processor,对称多处理结构)。单主机架构
SMP服务器中有多个处理器对称工作,无主次之分。各处理器平等使用所有物理内存,对资源的权限和访问效率都一样,由单一操作系统来控制。由于多处理器在内存访问上是完全平等的,所以SMP也被称为一致性内存访问(UMA, Uniform Memory Access)。
架构示意图:
SMP的资源共享特质也导致了其扩展存在瓶颈:每一个共享的环节出现瓶颈都会影响整个SMP服务器,尤其是内存!每一个处理器必须通过同一条内存总线访问同一处内存资源,所以随着处理器数量的增加,内存访问、总线使用的冲突就会迅速增加,最终造成处理器资源的浪费,使得处理器性能的有效性大大降低。
**NUMA(**Non-Uniform Memory Access,非一致性内存访问)
SMP这样的一致性内存访问结构由于公用总线等资源,存在扩展上的限制。而NUMA为提高扩展性提供了一种方案。
由多个CPU模块组成,CPU模块也称为NUMA节点。每个节点可以包括多个处理器,有独立的本地内存,I/O插槽。 NUMA节点间通过互联模块(称为Crossbar Switch)进行连接和信息交互,所以每个CPU可以访问整个系统的内存。显然,访问本地内存速度将远高于访问其他NUMA节点的内存。由于内存资源的不同存取代价,所以有了非一致性存储NUMA的名称。 基于本地内存与其他节点的内存的访问效率差异,开发应用程序应尽量减少不同NUMA节点之间的信息交互。
NUMA虽然相对SMP服务器扩展性较好,但是依然有局限,单个节点的内存容量有限,而访问远地内存的延时远远超过本地内存。当CPU数量增加时,系统性能无法线性增加。
**MPP(**Massive Parallel Processing,海量并行处理结构)
无论是SMP架构还是NUMA架构,作为单台服务器,扩展能力毕竟有限,MPP提供了使用SMP服务器作为节点的系统扩展方案。
单个节点是SMP服务器,所以MPP其实与SMP、NUMA服务器不同,其实由多台物理机组成。 不同节点间通过"节点互联网络"进行通信,"节点互联网络"仅供MPP服务器内部使用,对用户而言是透明的 用户角度只看到一个服务器系统 每个SMP服务器只访问本地的资源,不会跨节点资源占用。而节点间的信息交互,称为数据重分配(Data Redistribution)
这样的架构提供了完全无共享(Share Nothing)系统结构,理论上可以无限扩展!但是MPP服务器需要一种复杂的机制来调度和平衡各个节点的负载和并行处理过程。常用的一种做法是添加一层系统级软件来隔离这种复杂度对开发人员的影响,如特定数据库系统。这样开发人员只需要与数据库系统交互,而由数据库系统实现后代MPP系统节点间的调度、并行。
3.NUMA的缺点
尽管NUMA架构可以提高多处理器系统的性能和可扩展性,但也会引入一些问题,包括以下几个方面:
内存访问延迟不均衡:由于NUMA架构中不同节点的内存访问延迟不同,访问本地节点的内存速度通常比访问远程节点的内存速度更快。这可能导致在访问远程节点内存时出现延迟增加的情况,从而影响系统性能。 内存亲和性:NUMA架构下,操作系统和应用程序需要考虑将数据放置在适当的节点上,以尽量减少跨节点访问。这需要在程序设计和调度策略中考虑内存亲和性,以避免性能下降。 进程迁移开销:当一个进程从一个节点迁移到另一个节点时,需要将其相关的数据从一个节点的内存迁移到另一个节点的内存。这会导致额外的数据传输开销和延迟,并且可能影响应用程序的性能。 内存碎片化:由于不同节点之间的内存是独立管理的,节点之间的内存分配可能会导致内存碎片化。这可能导致内存利用率降低,并可能影响系统性能和可扩展性。
为了解决NUMA带来的问题,操作系统和应用程序需要采取相应的优化措施。例如,可以使用合适的内存分配策略和调度算法来最大程度地减少跨节点访问,减少内存延迟。此外,针对NUMA架构进行编程和调优,以最大程度地利用节点之间的内存亲和性,也是一种常见的优化方法。
一些案例
MySQL – The MySQL “swap insanity” problem and the effects of the NUMA architecture PostgreSQL – PostgreSQL, NUMA and zone reclaim mode on linux Oracle – Non-Uniform Memory Access (NUMA) architecture with Oracle database by examples Java – Optimizing Linux Memory Management for Low-latency / High-throughput Databases
4.NUMA工具推荐
numastat
numastat用来查看进程或者整个系统的内存消耗在各个NUMA节点的分布情况:
[root@localhost ~]# numastat
node0 node1
numa_hit 2032005 369189
numa_miss 0 0
numa_foreign 0 0
interleave_hit 23838 24012
local_node 2031767 333279
other_node 238 35910
numa_hit表示成功地从该节点分配到的内存页数。 numa_miss表示成功地从该节点分配到的内存页数,但其本意是希望从别的节点分配,失败以后退而求其次从改节点分配。 numa_foreign与numa_miss互为“影子”,每个numa_miss都来自另一个节点的numa_foreign。 Interleave_hit,有时候内存请求是没有NUMA节点偏好的,此时会均匀分配自各个节点(interleave),这个数值就是这种情况下从该节点分配出去的内存页面数。 local_node表示分配给运行在同一节点的进程的内存页数 other_node与上面相反。local_node值加上other_node值就是numa_hit值。
以上数值默认都是内存页数,要看具体多少MB可以通过加上-n参数实现。
[root@localhost ~]# numastat -n
Per-node numastat info (in MBs):
Node 0 Node 1 Total
--------------- --------------- ---------------
Numa_Hit 8256.67 1574.98 9831.64
Numa_Miss 0.00 0.00 0.00
Numa_Foreign 0.00 0.00 0.00
Interleave_Hit 93.12 93.80 186.91
Local_Node 8255.74 1434.70 9690.44
Other_Node 0.93 140.27 141.20
numastat还可以只看某些进程,甚至只要名字片段匹配。例如看QEMU进程的内存分布情况,可以通过numastat qemu
即可。
[root@localhost ~]# numastat qemu
Per-node process memory usage (in MBs)
PID Node 0 Node 1 Total
---------------- --------------- --------------- ---------------
2681 (qemu-kvm) 118.16 963.35 1081.51
2891 (qemu-kvm) 215.45 844.11 1059.55
2939 (qemu-kvm) 417.84 649.43 1067.27
2987 (qemu-kvm) 342.42 1048.06 1390.48
3039 (qemu-kvm) 16433.61 3.44 16437.05
3088 (qemu-kvm) 257.98 858.12 1116.10
3136 (qemu-kvm) 1097.77 238.20 1335.96
3182 (qemu-kvm) 896.34 431.20 1327.54
3229 (qemu-kvm) 493.41 705.16 1198.57
---------------- --------------- --------------- ---------------
Total 20272.98 5741.05 26014.03
[root@localhost ~]# numastat qemu -n
Per-node process memory usage (in MBs)
PID Node 0 Node 1 Total
---------------- --------------- --------------- ---------------
2681 (qemu-kvm) 118.16 963.35 1081.51
2891 (qemu-kvm) 215.34 844.21 1059.55
2939 (qemu-kvm) 417.82 649.45 1067.27
2987 (qemu-kvm) 341.39 1049.09 1390.48
3039 (qemu-kvm) 16433.64 3.41 16437.05
3088 (qemu-kvm) 257.94 858.16 1116.10
3136 (qemu-kvm) 1097.71 238.25 1335.96
3182 (qemu-kvm) 897.84 429.70 1327.54
3229 (qemu-kvm) 493.41 705.16 1198.57
---------------- --------------- --------------- ---------------
Total 20273.25 5740.77 26014.03
Per-node numastat info (in MBs):
Node 0 Node 1 Total
--------------- --------------- ---------------
Numa_Hit 8379.65 1749.08 10128.73
Numa_Miss 0.00 0.00 0.00
Numa_Foreign 0.00 0.00 0.00
Interleave_Hit 93.12 93.80 186.91
Local_Node 8378.72 1608.80 9987.52
Other_Node 0.93 140.27 141.20
numad
numad是一个可以自动管理NUMA亲和性(affinity)的工具(同时也是一个后台进程)。它实时监控NUMA拓扑结构(topology)和资源使用,并动态调整。同时它还可以在启动一个程序前,提供NUMA优化建议。
可以通过man查看其使用,由于numad讨论起来可能比较长篇大论,这里就不做展开了