前言

在进行Redis性能分析的时候,通常我们会考虑下面这些方面,如:

  • 缩短 key 的长度

  • 禁止使用 keys *

    我们都知道 keys *, 在使用的时候 Redis 会处于阻塞状态,导致其它任何命令在你的 Redis 实例中都无法执行。这个情况在 Redis 数据量大的时候就很明显,严重影响系统的运行。(一般我们用 scan 来代替)

  • 进行数据压缩

    在把数据存入 Redis 中,我们一般不会使用完整全名的数据,一般会进行适当的数据压缩,这样可以提高 Redis 性能,方便我们数据的储存。

  • 设置过期时间

    我们对一些不是永久性需要的数据,可以进行键的过期时间设置,这样到时间后,数据就会自动清除,节省我们 Redis 存储空间(内存)。

  • 使用回收策略

    为数据设置相关的过期回收策略,节省内存的开销,提高 Redis 运行的性能。( Redis 目前有8种回收策略,有兴趣可以查看 redis.conf ,多了LFU)。

  • 适当使用 bit (位图)

    适当使用 bit,可节省我们 Redis 存储的成本,即内存的大小。

  • 对所存储的数据字段进行优化

    如:我们只需要在 Redis 存储关键信息即可,详细信息存储到磁盘上即可。

  • 使用管道进行数据操作

    对于命令执行操作,我们要使用管道 pipeline,这样可以节省 Redis 传输过程的成本,提高 Redis 的性能。我们知道如果不适用管道,命令是一个一个进行操作,如果我们加上管道,这样由原来的单条命令变成多条命令进行传输操作,节省多次传输过程的网络开销。

emm.... (还有很多很多~~)

CPU结构对Redis性能的影响

但是,我们可能有时候会真正忽略 Redis 运行的前提条件,​​单核 CPU​​​ 和 ​​多核 CPU​​ 对 Redis 性能影响也是相差甚远。

在计算机组成原理中,我们都知道 CPU 是计算机的核心构成之一,中央处理器(Central Processing Unit),是计算机系统的​​运算​​​和​​控制​​中心。

一个CPU处理器中一般包含有多个​​运行核心​​​(物理核),运行核心我们也叫作物理核,一般包含​​一级缓存​​​(L1 Cache)和​​二级缓存​​(L2 Cache)。

其架构图如下所示:

CPU结构居然会影响Redis性能_数据

对于 ​​L1​​​ 缓存和 ​​L2​​​ 缓存,在每个物理核上都是独自拥有的,访问速度非常快,基本都在 ​​ns​​ 级别。我们设想如果把数据运行的指令放在这两个缓存上,那么可以大大提高计算机的访问性能。

这样我们可以设想这样一个情形:

如果,我们把 Redis 实例的​​数据​​​和​​指令​​绑定到一个 CPU 核上,那么当 Redis 频繁执行数据访问和操作时,都是基于CPU 上的缓存进行操作,那么性能是不是大大的提高了,没错,事实就是如此。

但是,我们电脑一般都是多核 CPU 的,在进行数据访问和操作时,系统不会只有一个线程在进行操作,是有很多很多的线程在同时进行操作,会同时操作我们的CPU,也就是我们所说的多线程操作CPU。

如果一个线程此时在​​CPU1​​​上运行,后来又跑到了​​CPU2​​​上运行,这时在CPU1上保留的数据和指令不在CPU2,这时要重新进行数据加载,会降低线程执行的效率,上述所发生的过程,我们也叫作​​上下文切换​​,这在操作系统内核环境下,是很常见的现象。

所以,我们要避免线程来回在CPU上进行切换,导致指令和数据进行多次加载,增加锁处理的时间。

我们从CPU结构出发,如果在多核CPU上,如果我们的每个Redis实例都只在一个CPU上运行的话,那么我们离解决问题的步伐是不是又更近了一步。(问题都是一步一步的剖析,慢慢解开其真容(​╹▽╹​))。

对相关进程进行绑定,我们可以使用  ​​taskset​

taskset

taskset 是依据​​线程PID​​​(TID)查询或设置线程的​​CPU亲和性​​​(Affiliation)(​​与哪个CPU核心绑定​​)。

如果有伙伴们不知道 taskset 如何使用,没关系,可以使用 ​​man​​​ 或者 ​​help​​ 手册进行查看相关参数使用(  man taskset  或 taskset -h )。

在进行绑定的时候,我们要知道自己机器的​​CPU的核数​​​( cat /proc/cpuinfo ),以方便我们准确的进行​​CPU绑定​​,不会说不知道自己CPU核数随便绑定一个超过自己CPU核数的数。

例子:假如我们要绑定CPU0这个CPU核,那么命令如下:

taskset -c 0 ./redis-server

这时,我们可以通过 Redis 的压测工具进行相关测试 ​​redis-benchmark​

例如:对 GET 、PUT 和 SET 进行测试:

redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000 -t get

redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000 -t put

redis-benchmark -h 127.0.0.1 -p 6379 -c 50 -n 10000 -t set

可以发现Redis实例的性能大大提升。

Redis实例还可以和网络中断程序绑在 ​​CPU Socket​​​ 上,这样能减小Redis 跨 Socket 访问内存的​​网络开销​​。(在网络传输过程中,这也是一个非常值得考虑优化的问题)。

这里或许会有伙伴会问了(一个cpu物理核内部不是还有​​逻辑核​​吗,我们不应该绑定在逻辑核上吗?)

这个小伙伴思考的好!别急,我先给你们维基百科(面向搜索引擎)下这些知识点:

  • ​CPU​​:中央处理单元,记住:CPU不等于物理核,也不等于逻辑核。

  • ​物理核​​:真实的cpu核,可以单独执行指令,由独立电路元件实体以及L1、L2缓存构成。

  • ​逻辑核(LCPU)​​:在一个物理核内,逻辑层面的核。(内部物理核通过高速运算诞生的概念)。

  • ​超线程(HT)​​:超线程可以在一个逻辑核等待指令执行的间隔(等待从cache或内存中获取下一条指令),把时间片分配到另一个逻辑核。高速在这两个逻辑核之间切换,让应用程序感知不到这个间隔,误认为自己是独占了一个核。

注意啦!!这里里面的三角关系:

一个CPU可以有多个物理核。但是如果操作系统开启了超线程,一个物理核可以分成 n 个逻辑核,n为超线程的数量。(分身)

来让我们看看单核CPU的草图:

CPU结构居然会影响Redis性能_数据_02

我们可以从上图看出,一个CPU核内在没有开启​​超线程​​​的时候 ,内部是有​​两个逻辑核​​​的,但是为什么我们不把 Redis 实例绑定在其中一个逻辑核上,而是绑定在它们的​​物理核​​上呢?

把 Redis实例绑定在一个物理核上,可以让该实例的​​主进程​​​、​​子进程​​​、​​后台线程​​​都共享这个物理核内的​​两个逻辑核​​​,这样可以使这些线程和进程不必只争抢一个逻辑核,一定程度上避免的​​CPU竞争​​。(因为内部有两个供他们选择使用,不会只因为使用一个而来回切换)。

总结

以上这些操作,都是小小的起步,如果我们还需要进一步提升Redis性能,我们需要从源码程度去解读Redis,深入研究,在必要时刻我们可以修改Redis的源码,从根源上寻找适合当前问题最佳扳手。

CPU结构居然会影响Redis性能_缓存_03