持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第22天,点击查看活动详情
前言
我们都知道redis基于内存,因此很容易联想到内存及网络是影响Redis性能的主要硬件因素。但我说CPU的结构也会影响到Redis性能,你知道为什么吗?我们往下看寻找答案。
主流的CPU架构
一个CPU有多个运行核心,一个运行核心称为一个物理核,每个物理核都可以运行应用程序。
每个物理核有 私有的一级缓存L1和二级缓存L2。访问他们的速度很快,L1延迟不超过10纳秒,L2的访存延迟在百纳秒级别。
除了L1和L2, 不同的物理核还会共享一个三级缓存L3。 L3能缓存的资源较大,能达到几MB到几十MB。当 L1、L2 缓存中没有数据缓存时,可以访问L3,这样就尽可能地避免了访问内存。
而且,主流的服务器上,通常会有多个CPU处理器(CPU Socket),不同处理器间通过总线连接。
在多 CPU 架构上,应用可以在不同的处理器上运行。
如果应用程序先在一个 Socket 上运行,并且把数据保存到了内存,然后被调度到另一个 Socket 上运行,此时,应用程序再进行内存访问时,就需要访问之前 Socket 上连接的内存,这种访问属于远端内存访问。和访问 Socket 直接连接的内存相比,远端内存访问会增加应用程序的延迟。
一个应用程序访问所在socket的本地内存和访问远端内存的延迟不一致,这个架构称为非统一内存访问架构(NUMA架构)。
CPU架构对应用程序的影响总结:
-
L1、L2速度很快,充分利用可缩短执行时间;
-
在NUMA架构下,若程序从一个socket上调度到另一个socket上,会出现远端内存访问的情况,会直接增加应用程序的执行时间。
CPU多核对redis性能的影响
在 CPU 多核场景下,Redis 实例被频繁调度到不同 CPU 核上运行的话,那么,对 Redis 实例的请求处理时间影响就更大了。因为每调度一次,一些请求就会受到运行时信息、指令和数据重新加载过程的影响,这就会导致某些请求的延迟明显高于其他请求。
要避免Redis总在不同CPU核上来回调度执行,我们可将redis实例和cpu核绑定,这样Redis实例会固定运行在一个CPU核上。命令就是:taskset
比如将Redis实例绑定在0号核上:
taskset -c 0 ./redis-server
绑定之后,可有效降低Redis的延迟,也能降低平均延迟以及提升吞吐率。
CPU的NUMA架构对Redis性能的影响
Redis 实例和网络中断程序的数据交互:网络中断处理程序从网卡硬件中读取数据,并把数据写入到操作系统内核维护的一块内存缓冲区。内核会通过 epoll 机制触发事件,通知 Redis 实例,Redis 实例再把数据从内核的内存缓冲区拷贝到自己的内存空间,如下图所示:
在NUMA架构下,当网络中断处理程序、Redis 实例分别和 CPU 核绑定后,就会有一个潜在的风险:如果网络中断处理程序和 Redis 实例各自所绑的 CPU 核不在同一个 CPU Socket 上,那么,Redis 实例读取网络数据时,就需要跨 CPU Socket 访问内存,这个过程会花费较多时间。
和访问 CPU Socket 本地内存相比,跨 CPU Socket 的内存访问延迟最高增加了接近20%,这就会导致 Redis 处理请求的延迟增加很多。
我们为了避免Redis和CPU Socket访问网络数据,最好将网络中断程序和 Redis 实例绑在同一个 CPU Socket 上。
需要注意的是: 在 CPU 的 NUMA 架构下,对 CPU 核的编号规则,并不是先把一个 CPU Socket 中的所有逻辑核编完,再对下一个 CPU Socket 中的逻辑核编码,而是先给每个 CPU Socket 中每个物理核的第一个逻辑核依次编号,再给每个 CPU Socket 中的物理核的第二个逻辑核依次编号.
通过lscpu可查看到核的编号。而且要注意 NUMA 架构下 CPU 核的编号方法,这样才不会绑错核。
绑核的风险和解决方案
当我们把 Redis 实例绑到一个 CPU 逻辑核上时,就会导致子进程、后台线程和 Redis 主线程竞争 CPU 资源,一旦子进程或后台线程占用 CPU 时,主线程就会被阻塞,导致 Redis 请求延迟增加。
解决方案有2种,分别是一个 Redis 实例对应绑一个物理核、修改 Redis 源码。
方案1:一个 Redis 实例对应绑一个物理核
在给 Redis 实例绑核时,我们不要把一个实例和一个逻辑核绑定,而要和一个物理核绑定,也就是说,把一个物理核的 2 个逻辑核都用上。
示例:taskset -c 0,12 ./redis-server
redis实例和物理核绑定,可让主线程、子进程、后台线程共享2个逻辑核,可在一定程度上缓解CPU资源竞争。
但只用了2个逻辑核,CPU的竞争仍会存在,可考虑另一种方案。
方案2:修改Redis源码
修改Redis源码,将子进程和后台线程绑到不同的CPU核上。 绑核要用到系统提供的数据结构cpu_set_t和3个函数CPU_ZERO、CPU_SET 和 sched_setaffinity。具体操作我们可查阅相关资料。
小结
本文讲解了CPU结构对Redis性能影响的原因。当我们绑核选择修改Redis源码时,需要了解修改源码带来的影响,最好在修改前记录这个动作。