Redis|17 为什么 CPU 结构也会影响 Redis的性能()

CPU 的多核架构及其多 CPU 架构也会对 Redis 在性能上有影响,如果在不了解 CPU 对 Redis 性能的影响的情况下,在对 Redis 进行性能上的调优的时候,可能就会忽略掉一些调优的方法,使得 Redis 的性能发挥到极致。因此下面会介绍一些主流的 CPU 的架构,CPU 多核架构以及多 CPU 架构在 Redis 性能调优的方法。
一、主流 CPU 架构

  • 一个处理器通常有多个运行核心,每个运行核心被称之为“物理核”,而每个物理核中都各自会有私有的两个缓存空间,被称之为“一级缓存”(Level 1 cache,简称 L1 cache) 和“二级缓存”(Level 2 cache,简称 L2 cache)。其中“一级缓存”中存储的数据又分为两种:一级数据缓存和一级指令缓存。由于各个物理核的L1, L2是物理核私有的,其他的物理核并不能访问该缓存中存储的数据,缓存在这一部分的数据的访问的速度一般是在几纳秒,非常的快,但是受处理器的构造技术的限制,一般容量比较小,通常就是其容量的大小只有 KB 级别,但是当 Redis 将指令或者是数据缓存在这些缓存中,其访问的效率非常高,反之如果在L1, L2缓存中未能找到需要的指令或者数据就需要去访问内存,而访问内存的消耗的时间一般是访问这些缓存(L1,L2)中的十倍左右,因此就影响了 Redis 的性能。所有不同的物理核之间还会共享使用一个“三级缓存”,三级缓存的容量比较大,通常有几 MB 或者几十 MB 的容量,这样一来就可以存储更多的数据,减少 Redis 去访问内存的次数,提高 Redis 的性能。
  • 另外每个物理核通常会运行两个“超级线程”,被称之为“逻辑核”。同一个物理核的两个逻辑核共享使用该物理核的一级和二级缓存。Redis|17 为什么 CPU 结构也会影响 Redis的性能()
    文章图片

    一般主流的 CPU 架构中,每个 CPU 内部会有 10 - 20 个物理核,同时为了提升服务器的性能,服务器上一般会使用一组 CPU(也称之为:多 CPU Socket),每个 CPU 都有多个物理核(每个物理核中包括了自己私有的一级二级缓存),三级缓存,以及连接的内存,同时不同的 CPU 使用的是总线进行连接。
    Redis|17 为什么 CPU 结构也会影响 Redis的性能()
    文章图片
  • 以上就是“多 CPU 架构”一个简单的模型,应用程序可以到任意的 CPU 上运行,也可以先到 CPU Socked1 上运行一段时间之后,又到 CPU Socked2 上运行,但是此时应用程序在进行内存访问的时候就需要使用“远程内存访问”,去访问之前的 CPU Socked1 所连接的内存进行访问,这与访问与 CPU Socked 直接连接的内存相比会增加应用程序的延迟。
  • 在多 CPU 架构中,一个应用程序在访问运行所在的 Socked 直接连接的内存和远程访问内存的延迟并不一致,因此我们将这种架构统称为“非统一内存访问架构”(Non-Uniform Memory Access简称 NUMA 架构)。
二、CPU 多核对 Redis 的性能的影响
  • 在一个 CPU 上运行的应用程序需要记录自己使用的软硬件资源的信息(例如:栈指针,CPU中寄存器中的值等),这些数据就称之为是运行时信息,同时为了提升应用程序的性能,将应用程序频繁使用的指令及其命令存储到一级或二级缓存中去,以提升应用程序的执行速度。但是,在多核 CPU 的场景下,一旦应用程序需要在一个新的 CPU 核上运行,那么,运行时信息就需要重新加载到新的 CPU 核上。而且,新的 CPU 核的 L1、L2 缓存也需要重新加载数据和指令,这会导致程序的运行时间增加。当数据加载完之后,应用程序才能在新的 CPU 上,Redis 才能开始处理请求。然而这一操作就需要消耗一些额外的时间,导致一些请求的响应延迟增加,使得请求的处理时间增加。
  • 在实际的应用场景中,如果我们需要对某些应用程序在性能上进行优化提升,就可以使用 teskset 命令将应用程序与指定的物理核进行绑定,这样一来就可以减少进行上下文切换重新加载数据带来的性能上的影响。
三、CPU 的 NUMA 架构对 Redis 性能的影响 在实际的应用场景中,为了提升 Redis 的网络性能,会将操纵系统的“网络中断程序”与 CPU 核进行绑定,这样做的目的使得避免了网络中断处理程序在不同的物理核来回切换运行,的确有效的提升了 Redis 的网络处理性能。但是网络处理程序是需要和 Redis 实例进行网络交互的,因此将网络处理程序与 CPU 核进行绑定的时候就要考虑要绑定在哪个 CPU 的物理核上,这将直接影响到 Redis 实例在访问网络数据的效率。
Redis|17 为什么 CPU 结构也会影响 Redis的性能()
文章图片

  • 上面的图例是 Redis 实例在向网络处理程序获取网络数据的过程:首先网络处理程序从网卡读取数据,之后在将获取到的网络数据写入到自己所运行的 CPU 内核的维护的那一块内存缓冲区中,之后内核通过 epoll 机制触发事件,通知 Redis 实例,Redis 实例再从网络处理程序所在的 CPU 内核的内存缓冲区中读取网络数据。
  • 在 CPU NUMA 架构中,会存在多个 CPU 通过总线连接在一起,如果此时网络处理程序与 Redis 实例所运行的物理核不是同一个 CPU Socket 下,就会导致需要 Redis 实例需要经过跨 CPU Socket 访问内存中的数据,这无疑会增加访问数据消耗的时间。如下图所示:当通知 Redis 实例在与网络处理程序不一样的 CPU Socket 上的逻辑核绑定了,那么 Redis 实例就需要通过总线将访问数据的命令发送到另一个 CPU Socket 上面,进行一个远程的访问,需要大量的时间上的开销。
    Redis|17 为什么 CPU 结构也会影响 Redis的性能()
    文章图片

    因此为了提升 Redis 实例的网络性能,就需要将 Redis 实例核网络处理程序绑定在使用一个 CPU Socket 下的不同的物理核上运行,这样就避免了 Redis 实例需要经过跨 CPU Socket 访问网络数据上的开销,只需要从自己所属的 CPU 的本地内存中访问网络数据即可。
    Redis|17 为什么 CPU 结构也会影响 Redis的性能()
    文章图片

    当进行绑定指定的 CPU 内核的时候需要指定 CPU 物理核的编号,在 CPU 的 NUMA 架构下,对 CPU 核的编号规则,并不是先把一个 CPU Socket 中的所有逻辑核编完,再对下一个 CPU Socket 中的逻辑核编码,而是先给每个 CPU Socket 中每个物理核的第一个逻辑核依次编号,再给每个 CPU Socket 中的物理核的第二个逻辑核依次编号。所以再编定的时候避免不清楚 NUMA 编码的规则导致绑定错误,使得 Redis 实例与网络处理程序绑定在了不同的 CPU Socket 上的逻辑核上,因此降低了 Redis 实例的网络性能。
四、绑核的风险和解决方案 Redis 实例除了主线程以外,还有需要进行生成 RDB 文件以及负责 AOF 日志重写的子进程以及其他的 Redis 后台线程,当 Redis 实例固定绑定在一个 CPU Socket 的逻辑核上,就避免不了这些子进程与其他的后台线程与主线程竞争 CPU 资源,导致 Redis 主线程被阻塞,进而使得 Redis 的请求延迟增加。针对这个问题,有两种解决方案:① 一个 Redis 实例对应绑定一个物理核;② 优化 Redis 源码。
(1)一个 Redis 实例对应绑定一个物理核 【Redis|17 为什么 CPU 结构也会影响 Redis的性能()】在上面的 CPU 的 NUMA 架构中,为了提升 Redis 实例的网络性能,因此将 Redis 实例和网络中断程序绑定在同一个 CPU Socket 的不同的逻辑核上,但是这样使得一个 Redis 实例上的主线程,子进程,后台线程共享同一个逻辑核,导致竞争 CPU 资源现象严重,会因此阻塞主线程,增加 Redis 请求的延迟。因此就让 Redis 实例不再只绑定一个 CPU 上的单一的逻辑核,而是选择绑定同一个 CPU Socket(物理核) 下的两个的逻辑核,这样 Redis 实例中竞争的不在只有一个逻辑核,从而减缓 CPU 的竞争的压力,但是仍然会存在主线程去竞争 CPU 资源导致阻塞的现象,因此还有另一个解决方案:优化 Redis 源码。
(2)优化 Redis 源码 通过修改 Redis 的源码,将 Redis 实例中的子进程和后台线程绑定在与主线程不同的 CPU 逻辑核中,这样就避免了 Redis 实例中主线程与其他子进程,后台线程竞争 CPU 资源的现象。熟悉 Redis 源码的话,可以自行修改 Redis 的源码进行 Redis 子进程和后台线程的绑核操作,如果不熟悉 Redis 源码的情况下,在 Redis 6.0 版本中就有支持绑核操作的配置了,这个会在 38 节中有介绍。
降低 Redis 的请求延迟是优化 Redis 的最终目标,其中多核 CPU 和 NUMA 架构是目前 Redis 中比较主流的架构,其中对于绑核优化的操作需要熟知。

    推荐阅读