CPU与redis性能

CPU的架构

一个 CPU 处理器中一般有多个物理核,每个物理核都可以运行应用程序。每个物理核都拥有私有的一级缓存,它包括一级指令缓存和一级数据缓存,以及私有的二级缓存。L1和L2是私有的, 所以访问基本在纳秒级。但是L1和L2空间并不大,所以还有一个各个物理核共享的三级缓存L3。

另外,每个物理核通常都会运行两个超线程,也叫作逻辑核。同一个物理核的逻辑核会共享使用 L1、L2 缓存。

在主流的服务器上,一个 CPU 处理器会有多个物理核。同时,服务器上通常还会有多个 CPU 处理器(也称为多 CPU Socket),每个处理器有自己的物理核(包括 L1、L2 缓存),L3 缓存,以及连接的内存,同时,不同处理器间通过总线连接。

应用程序可以在不同的处理器上运行。比如Redis 可以先在Socket 1 上运行一段时间,然后再被调度到 Socket 2 上运行,这个就是远端内存访问,和访问 Socket 直接连接的内存相比,远端内存访问会增加应用程序的延迟。

一个应用程序访问所在 Socket 的本地内存和访问远端内存的延迟并不一致,这个多CPU架构称为非统一内存访问架构(Non-Uniform Memory Access,NUMA 架构)。

CPU 多核对 Redis 性能的影响

context switch 是指线程的上下文切换,在 CPU 多核的环境中,一个线程先在一个 CPU 核上运行,之后又切换到另一个 CPU 核上运行,这时就会发生 context switch。如果Redis 主线程的运行时信息需要被重新加载到另一个 CPU 核上,而且,此时,另一个 CPU 核上的 L1、L2 缓存中,并没有 Redis 实例之前运行时频繁访问的指令和数据,所以,这些指令和数据都需要重新从 L3 缓存,甚至是内存中加载。这样就要耗费一些时间。

方案:把redis实例和CPU核绑定,使用taskset 命令

taskset -c 0 ./redis-server

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

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

如果网络中断处理程序和 Redis 实例各自所绑的 CPU 核不在同一个 CPU Socket 上,那么,Redis 实例读取网络数据时,就需要跨 CPU Socket 访问内存,这个过程会花费较多时间。

         网络中断处理程序被绑在了 CPU Socket 1 的某个核上,而 Redis 实例则被绑在了 CPU Socket 2 上。此时,网络中断处理程序读取到的网络数据,被保存在CPU Socket 1 的本地内存中,当 Redis 实例要访问网络数据时,就需要 Socket 2 通过总线把内存访问命令发送到 Socket 1 上,进行远程访问,时间开销比较大。

         为了避免这种情况,可以把网络中断程序和redis实例绑到同一个CPU Socket上。

绑核的风险

         导致子进程、后台线程和 Redis 主线程竞争 CPU 资源,一旦子进程或后台线程占用 CPU 时,主线程就会被阻塞,导致Redis 请求延迟增加。.

解决方案:

1. 个实例和一个逻辑核绑定,而要和一个物理核绑定,也就是说,把一个物理核的 2 个逻辑核都用上。

taskset -c 0,12 ./redis-server

把0和12两个逻辑核都绑上,这两个逻辑核都在同一个物理核里面。

2.  修改redis源码,把子进程和后台线程绑到不同的 CPU 核上

先看四个函数

  • cpu_set_t 数据结构:是一个位图,每一位用来表示服务器上的一个 CPU 逻辑核。
  • CPU_ZERO 函数:以 cpu_set_t 结构的位图为输入参数,把位图中所有的位设置为 0。
  • CPU_SET 函数:以 CPU 逻辑核编号和 cpu_set_t 位图为参数,把位图中和输入的逻辑核编号对应的位设置为 1。
  • sched_setaffinity 函数:以进程 / 线程 ID 号和 cpu_set_t 为参数,检查 cpu_set_t 中哪一位为 1,就把输入的 ID 号所代表的进程 / 线程绑在对应的逻辑核上。

绑定的逻辑如下

//用fork创建一个子进程

pid_t p = fork();

if(p < 0){

printf(" fork error\n");

}

//子进程代码部分

else if(!p){

cpu_set_t cpuset; //创建位图变量

CPU_ZERO(&cpu_set); //位图变量所有位设置0

CPU_SET(3, &cpuset); //把位图的第3位设置为1

sched_setaffinity(0, sizeof(cpuset), &cpuset); //把程序绑定在3号逻辑核

//实际子进程工作

exit(0);

}

这样子就可以把RDB 和 AOF 日志重写的子进程绑到不同的逻辑核上了。 (Redis 6.0 出来后,可以支持 CPU 核绑定的配置操作了)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值