主流的 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 核绑定\
-
针对不同子进程抢占资源
- 手动绑核
- 修改源码绑定核
\