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

240 阅读3分钟

主流的 CPU 架构

  • CPU架构

    • 一个cpu一般有多个运行核心,一个运行核心称为一个物理核

      • 每个物理核都可以运行应用程序

      • 私有的一级缓存(kb级别大小)

        • 指令缓存
        • 数据缓存
      • 私有的二级缓存(kb级别大小)

    • 多个物理核间共享流l3(mb级别)

      • 内存之上的缓存
  • 通常情况下,一态服务器上有多个cpu处理器(cpu socket),每个cpu处理器下有多个物理核

  • 在多 CPU 架构上,应用程序可以在不同的处理器上运行 从socket1调度到socket2上\

    • 注意:不同的cpu访问的内存不同,远端访问内存会增加延迟
    • NUMA 架构(Non-Uniform Memory Access)非统一内存访问架构 一个应用程序访问所在 Socket 的本地内存和访问远端内存的延迟并不一致(可以公用一套操作系统,节省操作系统开销)

\

CPU 多核对 Redis 性能的影响

  • 运行时信息:在一个 CPU 核上运行时,应用程序需要记录自身使用的软硬件资源信息(例如栈指针、CPU 核的寄存器值等)\

    • 访问频繁的会缓存到L1和L2上,提升执行速度
    • 将该应用程序切换到别的cpu核上,需要重新加载指令,增加运行时间
  • context switch:线程上下文切换 从一个核切换到不同的核上运行\

  • 解决办法:使用taskset将程序绑定在一个核上运行

  • 指令:taskset -c 0 ./redis-server

CPU 的 NUMA 架构对 Redis 性能的影响

  • 把操作系统的网络中断处理程序和 CPU 核绑定\

    • 避免处理程序在不同核上切换
  • redis与网络中断程序的数据交互

    • 1.网络中断处理程序从网卡硬件中读取数据,并把数据写入到操作系统内核维护的一块内存缓冲区
    • 2.内核会通过 epoll 机制触发事件,通知 Redis 实例
    • 3.Redis 实例再把数据从内核的内存缓冲区拷贝到自己的内存空间
  • 在NUMA架构下

    • 如果网络中断处理程序和 Redis 实例各自所绑的 CPU 核不在同一个 CPU Socket 上\

    • 就需要跨 CPU Socket 访问内存,这个过程会花费较多时间\

    • 解决办法:把网络中断程序和 Redis 实例绑在同一个 CPU Socket 上

    • 每个物理核的编号规则:循环对每一个socket的每个一次编号

    • 建议你同时把 Redis 实例和网络中断程序绑在同一个 CPU Socket 的不同核上

\

绑核的风险和解决方案

  • redis的子线程

    • RDB 生成子进程\

    • AOF 重写子进程\

  • 把 Redis 实例绑到一个 CPU 逻辑核上时,就会导致子进程、后台线程和 Redis 主线程竞争 CPU 资源\

  • 解决方案

    • 一个 Redis 实例对应绑一个物理核,同时绑定多个逻辑核\

      • 指令:taskset -c 0,12 ./redis-server
    • 优化 Redis 源码

      • cpu_set_t(位图,每一位用来表示服务器的cpu逻辑核)三个函数\

        • CPU_ZERO 把图中所有位置设置为0\

        • CPU_SET 将对应位置设置为1\

        • sched_setaffinity  cpu_set_t 中哪一位为 1,就把输入的 ID 号所代表的进程 / 线程绑在对应的逻辑核上\

      • 创建过程

        • 创建一个 cpu_set_t 结构的位图变量\

        • 使用 CPU_ZERO 函数,把 cpu_set_t 结构的位图所有的位都设置为 0\

        • 使用 CPU_SET 函数,把 cpu_set_t 结构的位图相应位设置为 1\

        • 使用 sched_setaffinity 函数,把程序绑定在 cpu_set_t 结构位图中为 1 的逻辑核上\

总结

  • 在多核 CPU 架构下,Redis 如果在不同的核上运行,就需要频繁地进行上下文切换,这个过程会增加 Redis 的执行时间\

  • 把网络中断处理程序和 CPU 核绑定\

  • 针对不同子进程抢占资源

    • 手动绑核
    • 修改源码绑定核

\