NUMA架构

Linux 内核 101:NUMA架构

廖长江

廖长江

唯见长江天际流

32 人赞同了该文章

本文参考了以下文章、视频:

一句话

NUMA 指的是针对某个 CPU,内存访问的距离和时间是不一样的。是为了解决多 CPU 系统下共享 BUS 带来的性能问题。(这句话可能不太严谨,不是为了解决,而是事实上解决了。)

NUMA 架构图

从最简单的开始,一个 CPU(注意:这里指的是物理 CPU,不是核。需要注意的一点是NUMA 是针对多物理 CPU 而言的,而不是多核。),通过 bus 和 RAM 相连。

接下来多CPU 出现了(再说一次,不是多核单CPU!),如果还是像之前那样将所有的CPU 通过一个 BUS 和 RAM 相连,BUS 会成为性能的杀手。而且,加的 CPU 越多,性能损耗会越高。

这时候 NUMA 架构就展露身手了:通过把 CPU 和临近的 RAM 当做一个 node,CPU 会优先访问距离近的 RAM。同时,CPU 直接有一个快速通道连接,所以 每个CPU 还是访问到所有的 RAM 位置(只是速度会有差异)。

实际中,一个不一定单独占有一个 RAM,可以有下面这些很多种组合:

浅看 Linux 中的NUMA架构

以下操作在阿里云云服务器 Ubuntu18.04环境下运行

首先通过 dmesg|grep-i numa查看一下系统是不是支持numa:可见当前系统不支持 :)

然后使用一个工具: numactl,可以通过 apt install numactl进行安装。然后运行:

  1. numactl --hardware

还有一个实用工具: lstopo,通过 apt install lstopo

  1. lstopo --of png > server.png

从图中可以看到,有一个node 节点。为什么我的系统不支持 numa,Linux 还是把所有的 CPU 和所有的 RAM 组合到一起成为一个 node 呢?这不是闲的没事干吗?

关于这一点,《Understanding the Linux Kernel》一书里面这么说:

这主要是出于代码可扩展性的考虑,这样一套代码就可以在不支持 numa 和支持 numa 的环境下运行了。

如果是支持 numa 架构的服务器,看到的图会是这样的:

NUMA 对Linux 会产生什么影响?

系统 boot 的时候,硬件会把 numa 信息发送给 os,如果系统支持 numa,会发生以下几件事:

  • 获取 numa 配置信息
  • 将 processors(不是 cores) 分成很多 nodes,一般是一个 processor 一个 node。
  • 将 processor 附近的 memory 分配给它。
  • 计算node 间通信的cost(距离)。

如果你只是把 CPU 和内存当作是黑盒子,简单地期待它 work 的话,可能会发生意想不到的事情。

  • 每个进程、线程都会继承一个 numa policy,定义了可以使用那些CPU(甚至是那些 core),哪些内存可以使用,以及 policy 的强制程度,即是优先还是强制性只允许。
  • 每个 thread 被分配到了一个”优先” 的 node 上面运行,thread 可以在其他地方运行(如果 policy 允许的话),但是 os 会尝试让他在优先地 node 上面去运行。
  • 内存分配:默认内存从同一个 node 里面进行分配。
  • 在一个 node 上面分配地内存不会被移动到其他node。
上面两段翻译自:  https://blog.jcole.us/2010/09/28/mysql-swap-insanity-and-the-numa-architecture/ ,如有不清晰的地方,请移步原文。

看一个MySQL 的实例

优质好文,有能力的同学最好还是直接看原文:  https://blog.jcole.us/2010/09/28/mysql-swap-insanity-and-the-numa-architecture/ 我这里简单翻译一下。

文中提到了一个问题:在一个有64G 内存、2个 4核 CPU 的 Linux 服务器中运行 MySQL 服务,MySQL 配置了48G 的 innodb buffer pool。然后发现,尽管系统还有很多空余的内容,很多内存被 swap 出去了。

这带来了极大的性能问题,因为query 的时候如果需要的内容被 swap 出去了。。就需要再 load 回来。这也是困恼了MySQL 社区很长时间的问题。

前面说到 Linux 有一个 numa policy,这个是可以人为控制的。

  • —localalloc ,使用当前 node,默认。
  • --preferred=node,优先实用指定的 node,实在不行用其他的 nod 也可以。
  • --membind=nodes,总是使用人为指定一个或多个 nodes。
  • --interleaved=all,采用round-robin算法轮流使用不同的 node。

从 Linux os 的角度来看,MySQL 数据库就是一个进程,会优先让他在某一个 node 中运行。如果只是使用少部分内存,这没什么问题,但是当你要占用系统大多数内存的时候,问题就来了:

由于 os 会试图让你在某个『优先』的node里面运行,就会产生内存分配不均匀的情况:

Node0 已经快被占满了,Node1还剩下很多。由于 node0和 node1是独立的,尽管 node1里面有空余内存,node0里面的内存还是会被 swap 出去。这就是前面提到的问题的根源。

那么怎么解决呢?

  1. numactl --interleave all command

用前面提到的 --interleave all numa policy,将这段添加到 mysqld_save 语句前面。过后,内存分配就是均匀的了,内存足够的情况下,就不会出现异常的 swap 现象了。

当然这只是一个最简单粗暴的解决方案,还有其他更好的,原文有提及,但是不是本文的重点,所以就不深入探讨了。

总结一下

摩尔定律正在失效,当 CPU 的性能总会有极限的那一天,多 CPU 才是未来。作为一个有理想的搬砖工人,至少还是应该多了解一下多 CPU 系统架构。作为一个系统软件开发者,更应该熟悉多 CPU 架构,让自己开发的应用充分利用硬件福利。