参考资料:
《Redis为什么变慢了?一文讲透如何排查Redis性能问题 | 万字长文》
相关文章:
《Redis:内存淘汰机制》
《Redis:持久化RDB与AOF》
《Redis:主从复制》
写在开头:本文为学习后的总结,可能有不到位的地方,错误的地方,欢迎各位指正。
前言
在之前的文章中,我们介绍了redis的内部结构、使用方法等,这篇文章就来讲讲我们如何优化redis的使用。
深入理解本文需要对redis的相关内容有足够的了解,如果不熟悉的朋友可以先从我之前的文章开始看起(《Redis:底层数据结构》、《Redis:数据对象与底层实现》、《Redis:内存淘汰机制》、《Redis:持久化RDB与AOF》、《Redis:主从复制》)。
目录
前言
一、key使用不当
1、命令复杂度过高
排查方法
2、key的value过大
3、key集中过期
二、持久化占用资源过高
1、持久化异步线程fork过久
2、AOF配置
三、内存使用不当
1、实例内存达到上限
2、开启内存大页
3、碎片整理
4、使用Swap
总结
一、key使用不当
1、命令复杂度过高
如果你的应用程序执行的 Redis 命令有以下特点,那么有可能会导致操作延迟变大:
(1)经常使用 O(N) 以上复杂度的命令,例如 SORT、SUNION、ZUNIONSTORE 聚合类命令
这种情况下,Redis需要在操作内存数据,容易导致时间复杂度过高,花费较多的 CPU 资源。
(2)使用 O(N) 复杂度的命令,但 N 的值非常大
这种情况下虽然不需要像第一种情况那样消耗CPU资源,但是Redis 一次需要返回给客户端的数据过多,更多时间花费在数据协议的组装和网络传输过程中。
我们都知道,Redis 是单线程处理客户端请求的,如果你经常使用以上命令,那么当 Redis 处理客户端请求时,一旦前面某个命令发生耗时,就会导致后面的请求发生排队,对于客户端来说,响应延迟也会变长。
针对这种情况,最好的方法还是在业务端使用的时候进行优化:
- 尽量不使用 O(N) 以上复杂度过高的命令,对于数据的聚合操作,放在客户端做
- 执行 O(N) 命令,保证 N 尽量的小(推荐 N <= 300),每次获取尽量少的数据,让 Redis 可以及时处理返回
排查方法
Redis 提供了慢日志(slowlog)命令的统计功能,它记录了有哪些命令在执行时耗时比较久,我们可以通过如下方式设置慢日志的记录要求。
# 命令执行耗时超过 5 毫秒,记录慢日志
CONFIG SET slowlog-log-slower-than 5000
# 只保留最近 500 条慢日志
CONFIG SET slowlog-max-len 500
设置完成之后,所有执行的命令如果操作耗时超过了 5 毫秒,都会被 Redis 记录下来,然后我们就可以通过如下方式进行查看了。
127.0.0.1:6379> SLOWLOG get 5
1) 1) (integer) 32693 # 慢日志ID
2) (integer) 1593763337 # 执行时间戳
3) (integer) 5299 # 执行耗时(微秒)
4) 1) "LRANGE" # 具体执行的命令和参数
2) "user_list:2000"
3) "0"
4) "-1"
2) 1) (integer) 32692
2) (integer) 1593763337
3) (integer) 5044
4) 1) "GET"
2) "user_info:1000"
...
通过查看慢日志,我们就可以知道在什么时间点,执行了哪些命令比较耗时。
2、key的value过大
Redis 在写入数据时,需要为新的数据分配内存,相对应的,当从 Redis 中删除数据时,它会释放对应的内存空间。
如果一个 key 写入的 value 非常大,那么 Redis 在分配内存时就会比较耗时。同样的,当删除这个 key 时,释放内存也会比较耗时,这种类型的 key 我们一般称之为 bigkey。
如果你查询慢日志发现,并不是复杂度过高的命令导致的,而都是 SET / DEL 这种简单命令出现在慢日志中,那么你就要怀疑你的实例否写入了 bigkey。此时,你需要检查你的业务代码,是否存在写入 bigkey 的情况。你需要评估写入一个 key 的数据大小,尽量避免一个 key 存入过大的数据。
Redis 提供了扫描 bigkey 的命令,执行以下命令就可以扫描出,一个实例中 bigkey 的分布情况,输出结果是以类型维度展示的:
$ redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.01
...
-------- summary -------
Sampled 829675 keys in the keyspace!
Total key length in bytes is 10059825 (avg len 12.13)
Biggest string found 'key:291880' has 10 bytes
Biggest list found 'mylist:004' has 40 items
Biggest set found 'myset:2386' has 38 members
Biggest hash found 'myhash:3574' has 37 fields
Biggest zset found 'myzset:2704' has 42 members
36313 strings with 363130 bytes (04.38% of keys, avg size 10.00)
787393 lists with 896540 items (94.90% of keys, avg size 1.14)
1994 sets with 40052 members (00.24% of keys, avg size 20.09)
1990 hashs with 39632 fields (00.24% of keys, avg size 19.92)
1985 zsets with 39750 members (00.24% of keys, avg size 20.03)
从输出结果我们可以很清晰地看到,每种数据类型所占用的最大内存 / 拥有最多元素的 key 是哪一个,以及每种数据类型在整个实例中的占比和平均大小 / 元素数量。
其实,使用这个命令的原理,就是 Redis 在内部执行了 SCAN 命令,遍历整个实例中所有的 key,然后针对 key 的类型,分别执行 STRLEN、LLEN、HLEN、SCARD、ZCARD 命令,来获取 String 类型的长度、容器类型(List、Hash、Set、ZSet)的元素个数。
当执行这个命令时,要注意 2 个问题:
- 对线上实例进行 bigkey 扫描时,Redis 的 QPS 会突增,为了降低扫描过程中对 Redis 的影响,最好控制一下扫描的频率,指定 -i 参数即可,它表示扫描过程中每次扫描后休息的时间间隔,单位是秒
- 扫描结果中,对于容器类型(List、Hash、Set、ZSet)的 key,只能扫描出元素最多的 key。但一个 key 的元素多,不一定表示占用内存也多,你还需要根据业务情况,进一步评估内存占用情况
针对bigkey的优化,首先肯定是和上一节一样,业务上尽量避免bigkey的使用。如果是在现需要用到,在释放操作时可以使用如下方法进行优化:
- 如果你使用的 Redis 是 4.0 以上版本,用 UNLINK 命令替代 DEL,此命令可以把释放 key 内存的操作,放到后台线程中去执行,从而降低对 Redis 的影响
- 如果你使用的 Redis 是 6.0 以上版本,可以开启 lazy-free 机制(lazyfree-lazy-user-del = yes),在执行 DEL 命令时,释放内存也会放到后台线程中执行
关于unlink与lazy-free的相关内容介绍可以看我之前介绍内存淘汰的文章《Redis:内存淘汰机制》
3、key集中过期
如果有大量的 key 在某个固定时间点集中过期,在这个时间点访问 Redis 时,就有可能导致延时变大,这种情况也就是我们常说的缓存雪崩。
Redis 的过期数据采用被动过期 + 主动过期两种策略(详见《Redis:内存淘汰机制》):
被动过期:只有当访问某个 key 时,才判断这个 key 是否已过期,如果已过期,则从实例中删除
主动过期:Redis 内部维护了一个定时任务,默认每隔 100 毫秒(1秒10次)就会从全局的过期哈希表中随机取出 20 个 key,然后删除其中过期的 key,如果过期 key 的比例超过了 25%,则继续重复此过程,直到过期 key 的比例下降到 25% 以下,或者这次任务的执行耗时超过了 25 毫秒,才会退出循环
这个主动过期 key 的定时任务,是在 Redis 主线程中执行的。
也就是说如果在执行主动过期的过程中,出现了需要大量删除过期 key 的情况,那么此时应用程序在访问 Redis 时,必须要等待这个过期任务执行结束,Redis 才可以服务这个客户端请求。此时就会出现,应用访问 Redis 延时变大。
如果此时需要过期删除的是一个 bigkey,那么这个耗时会更久。而且,这个操作延迟的命令并不会记录在慢日志中。因为慢日志中只记录一个命令真正操作内存数据的耗时,而 Redis 主动删除过期 key 的逻辑,是在命令真正执行之前执行的。
所以,此时你会看到,慢日志中没有操作耗时的命令,但我们的应用程序却感知到了延迟变大,其实时间都花费在了删除过期 key 上,这种情况我们需要尤为注意。
此时,你需要检查你的业务代码,是否存在集中过期 key 的逻辑。一般集中过期使用的是 expireat / pexpireat 命令,你需要在代码中搜索这个关键字。
排查代码后,如果确实存在集中过期 key 的逻辑存在,但这种逻辑又是业务所必须的,那此时如何优化,同时又不对 Redis 有性能影响呢? 一般有两种方案来规避这个问题:
集中过期 key 增加一个随机过期时间,把集中过期的时间打散,降低 Redis 清理过期 key 的压力。
# 在过期时间点之后的 5 分钟内随机过期掉
redis.expireat(key, expire_time + random(300))
如果你使用的 Redis 是 4.0 以上版本,可以参考上一节的内容,开启 lazy-free 机制(注意bigkey),当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程。
# 释放过期 key 的内存,放到后台线程执行
lazyfree-lazy-expire yes
二、持久化占用资源过高
1、持久化异步线程fork过久
Redis为了保证数据的安全性,提供了RDB与AOF两种持久化方案,详细内容可以看这篇文章《Redis:持久化RDB与AOF》。
无论哪种持久化方式(包括AOF的重写),都支持异步线程生成持久化文件,主进程创建出一个子进程进行数据的持久化,这个过程会调用操作系统提供的 fork 函数。
当fork子进程时,子进程时会拷贝父进程的页表,即虚实映射关系(虚拟内存和物理内存的映射索引表),而不会拷贝物理内存。而且这个 fork 过程会消耗大量的 CPU 资源,在完成 fork 之前,整个 Redis 实例会被阻塞住,无法处理任何客户端请求。
如果此时你的 CPU 资源本来就很紧张,那么 fork 的耗时会更长,甚至达到秒级,这会严重影响 Redis 的性能。那如何确认确实是因为 fork 耗时导致的 Redis 延迟变大呢?你可以在 Redis 上执行 INFO 命令,查看 latest_fork_usec 项,单位微秒。
# 上一次 fork 耗时,单位微秒
latest_fork_usec:59477
这个时间就是主进程在 fork 子进程期间,整个实例阻塞无法处理客户端请求的时间。如果你发现这个耗时很久,就要警惕起来了,这意味在这期间,你的整个 Redis 实例都处于不可用的状态。
除了数据持久化会生成 RDB 之外,当主从节点第一次建立数据同步时,主节点也创建子进程生成 RDB,然后发给从节点进行一次全量同步,所以,这个过程也会对 Redis 产生性能影响。
要想避免这种情况,你可以采取以下方案进行优化:
- 控制 Redis 实例的内存:尽量在 10G 以下,执行 fork 的耗时与实例大小有关,实例越大,耗时越久
- 合理配置数据持久化策略:在 slave 节点执行 RDB 备份,推荐在低峰期执行,而对于丢失数据不敏感的业务(例如把 Redis 当做纯缓存使用),可以关闭 AOF 和 AOF rewrite
- 降低主从库全量同步的概率:适当调大 repl-backlog-size 参数,避免主从全量同步
2、AOF配置
AOF是不断的将最新的Redis指令落地到磁盘上来实现的持久化,因此会需要频繁的写数据,其工作原理如下:
- Redis 执行写命令后,把这个命令写入到 AOF 文件内存中(write 系统调用)
- Redis 根据配置的 AOF 刷盘策略,把 AOF 内存数据刷到磁盘上(fsync 系统调用)
为了保证 AOF 文件数据的安全性,Redis 提供了 3 种刷盘机制:
- always:主线程每次执行写操作后立即刷盘,此方案会占用比较大的磁盘 IO 资源,但数据安全性最高
- no:主线程每次写操作只写内存就返回,内存数据什么时候刷到磁盘,交由操作系统决定,此方案对性能影响最小,但数据安全性也最低,Redis 宕机时丢失的数据取决于操作系统刷盘时机
- everysec:主线程每次写操作只写内存就返回,然后由后台线程每隔 1 秒执行一次刷盘操作(触发fsync系统调用),此方案对性能影响相对较小,但当 Redis 宕机时会丢失 1 秒的数据
如果你的 AOF 配置为 always,那么 Redis 每处理一次写操作,都会把这个命令写入到磁盘中才返回,整个过程都是在主线程执行的,这个过程必然会加重 Redis 写负担。原因也很简单,操作磁盘要比操作内存慢几百倍,采用这个配置会严重拖慢 Redis 的性能,因此我不建议你把 AOF 刷盘方式配置为 always。
我们接着来看 no 配置项。在这种配置下,Redis 每次写操作只写内存,什么时候把内存中的数据刷到磁盘,交给操作系统决定,此方案对 Redis 的性能影响最小,但当 Redis 宕机时,会丢失一部分数据,为了数据的安全性,一般我们也不采取这种配置。(如果你的 Redis 只用作纯缓存,对于数据丢失不敏感,采用配置 no 也是可以的)
因此我们一般采用everysec,这个方案优势在于,Redis 主线程写完内存后就返回,具体的刷盘操作是放到后台线程中执行的,后台线程每隔 1 秒把内存中的数据刷到磁盘中,这种方案既兼顾了性能,又尽可能地保证了数据安全。
但是,这种方案也并不是完全不会产生问题的,试想这样一种情况:当 Redis 后台线程在执行 AOF 文件刷盘时,如果此时磁盘的 IO 负载很高,那这个后台线程在执行刷盘操作(fsync系统调用)时就会被阻塞住。
此时的主线程依旧会接收写请求,紧接着,主线程又需要把数据写到文件内存中(write 系统调用),但此时的后台子线程由于磁盘负载过高,导致 fsync 发生阻塞,迟迟不能返回,那主线程在执行 write 系统调用时,也会被阻塞住,直到后台线程 fsync 执行完成后,主线程执行 write 才能成功返回。在这个过程中,主线程依旧有阻塞的风险。
那什么情况下会导致磁盘 IO 负载过大?以及如何解决这个问题呢?
- 子进程正在执行 AOF rewrite,这个过程会占用大量的磁盘 IO 资源
- 有其他应用程序在执行大量的写文件操作,也会占用磁盘 IO 资源
对于情况1,说白了就是,Redis 的 AOF 后台子线程刷盘操作,撞上了子进程 AOF重写。为了避免这种情况,Redis 提供了一个配置项,当子进程在 AOF rewrite 期间,可以让后台子线程不执行刷盘(不触发 fsync 系统调用)操作。
# AOF rewrite 期间,AOF 后台子线程不进行刷盘操作
# 相当于在这期间,临时把 appendfsync 设置为了 none
no-appendfsync-on-rewrite yes
这相当于在 AOF rewrite 期间,临时把 appendfsync 设置为了 none,当然,开启这个配置项,在 AOF rewrite 期间,如果实例发生宕机,那么此时会丢失更多的数据,性能和数据安全性,你需要权衡后进行选择。
如果占用磁盘资源的是其他应用程序,那就比较简单了,你需要定位到是哪个应用程序在大量写磁盘,然后把这个应用程序迁移到其他机器上执行就好了,避免对 Redis 产生影响。
当然,如果你对 Redis 的性能和数据安全都有很高的要求,那么我建议从硬件层面来优化,更换为 SSD 磁盘,提高磁盘的 IO 能力,保证 AOF 期间有充足的磁盘资源可以使用。
三、内存使用不当
1、实例内存达到上限
当我们把 Redis 当做纯缓存使用时,通常会给这个实例设置一个内存上限 maxmemory,然后设置一个数据淘汰策略。而当实例的内存达到了 maxmemory 后,你可能会发现,在此之后每次写入新数据,操作延迟变大了。
原因在于,当 Redis 内存达到 maxmemory 后,每次写入新的数据之前,Redis 必须先从实例中踢出一部分数据,让整个实例的内存维持在 maxmemory 之下,然后才能把新数据写进来。
这个踢出旧数据的逻辑也是需要消耗时间的,而具体耗时的长短,要取决于你配置的淘汰策略:
- allkeys-lru:不管 key 是否设置了过期,淘汰最近最少访问的
- keyvolatile-lru:只淘汰最近最少访问、并设置了过期时间的
- keyallkeys-random:不管 key 是否设置了过期,随机淘汰
- keyvolatile-random:只随机淘汰设置了过期时间的
- keyallkeys-ttl:不管 key 是否设置了过期,淘汰即将过期的
- keynoeviction:不淘汰任何 key,实例内存达到 maxmeory 后,再写入新数据直接返回错误
- allkeys-lfu:不管 key 是否设置了过期,淘汰访问频率最低的 key(4.0+版本支持)volatile-lfu:只淘汰访问频率最低、并设置了过期时间 key(4.0+版本支持)
- volatile-lfu:只淘汰访问频率最低、并设置了过期时间 key(4.0+版本支持)
具体使用哪种策略,我们需要根据具体的业务场景来配置。
一般最常使用的是 allkeys-lru / volatile-lru 淘汰策略,它们的处理逻辑是,每次从实例中随机取出一批 key(这个数量可配置),然后淘汰一个最少访问的 key,之后把剩下的 key 暂存到一个池子中,继续随机取一批 key,并与之前池子中的 key 比较,再淘汰一个最少访问的 key。以此往复,直到实例内存降到 maxmemory 之下。(详细过程可以看我之前的文章《Redis:内存淘汰机制》)
需要注意的是,Redis 的淘汰数据的逻辑与删除过期 key 的一样,也是在命令真正执行之前执行的,也就是说它也会增加我们操作 Redis 的延迟,而且,写 QPS 越高,延迟也会越明显。
针对淘汰策略的优化,一般有以下几个建议:
- 避免存储 bigkey,降低释放内存的耗时
- 淘汰策略改为随机淘汰,随机淘汰比 LRU 要快很多(视业务情况调整)
- 拆分实例,把淘汰 key 的压力分摊到多个实例上
- 如果使用的是 Redis 4.0 以上版本,开启 layz-free 机制,把淘汰 key 释放内存的操作放到后台线程中执行。
2、开启内存大页
现代操作系统采用了分页式内存来实现了虚拟内存的扩展,使得内存的分配由早期的一次性完全分配变为了现在的按需分配的方式,需要多少就分配多少。应用程序向操作系统申请内存时,是按内存页进行申请的,而常规的内存页大小是 4KB。
Linux 内核从 2.6.38 开始,支持了内存大页机制,该机制允许应用程序以 2MB 大小为单位,向操作系统申请内存。应用程序每次向操作系统申请的内存单位变大了,但这也意味着申请内存的耗时变长。
以下持久化的内容详见《Redis:持久化RDB与AOF》
当持久化操作fork完后,子进程开始工作时,此时的主进程依旧是可以接收写请求的,而进来的写请求,会采用 Copy On Write(写时复制)的方式操作内存数据。
也就是说,主进程一旦有数据需要修改,Redis 并不会直接修改现有内存中的数据,而是先将这块内存数据拷贝出来,再修改这块新内存的数据,这就是所谓的「写时复制」。以理解成,谁需要发生写操作,谁就需要先拷贝,再修改。
这样做的好处是,父进程有任何写操作,并不会影响子进程的数据持久化(子进程只持久化 fork 这一瞬间整个实例中的所有数据即可,不关心新的数据变更,因为子进程只需要一份内存快照,然后持久化到磁盘上)。
但是请注意,主进程在拷贝内存数据时,这个阶段就涉及到新内存的申请,如果此时操作系统开启了内存大页,那么在此期间,客户端即便只修改 10B 的数据,Redis 在申请内存时也会以 2MB 为单位向操作系统申请,申请内存的耗时变长,进而导致每个写请求的延迟增加,影响到 Redis 性能。
操作系统提供的内存大页机制,其优势是,可以在一定程序上降低应用程序申请内存的次数,但是对于 Redis 这种对性能和延迟极其敏感的数据库来说,我们希望 Redis 在每次申请内存时,耗时尽量短,所以我不建议你在 Redis 机器上开启这个机制。
我们可以使用如下命令查看是否开启了内存大页机制
$ cat /sys/kernel/mm/transparent_hugepage/enabled
[always] madvise never
如果输出选项是 always,就表示目前开启了内存大页机制,我们需要关掉它:
$ echo never > /sys/kernel/mm/transparent_hugepage/enabled
3、碎片整理
Redis 存储存储数据的时候向操作系统申请的内存空间可能会大于数据实际需要的存储空间,以避免后续扩容时再次申请,更重要的是,Redis倾向于申请一块连续的内存,这就导致我们上文提到的操作系统的分页优化无法适用。
一以下内容参考自《Redis 内存碎片详解》
举个例子:操作系统为你分配了 32 字节的连续内存空间,而你存储数据实际只需要使用 24 字节内存空间,那这多余出来的 8 字节内存空间如果后续没办法再被分配存储其他数据的话,就可以被称为内存碎片。
同时,Redis在删除某个key后也并不会立即释放这个key所占用的内存,因此这块内存在短期内也是无法再次利用的。
Redis 内存碎片虽然不会影响 Redis 性能,但会降低 Redis 的内存使用率,我们可以通过执行 INFO 命令,得到这个实例的内存碎片率:
# Memory
used_memory:5709194824
used_memory_human:5.32G
used_memory_rss:8264855552
used_memory_rss_human:7.70G
...
mem_fragmentation_ratio:1.45
内存碎片率是通过mem_fragmentation_ratio = used_memory_rss / used_memory计算得出,其中 used_memory 表示 Redis 存储数据的内存大小,而 used_memory_rss 表示操作系统实际分配给 Redis 进程的大小。
如果 mem_fragmentation_ratio > 1.5,说明内存碎片率已经超过了 50%,这时我们就需要采取一些措施来降低内存碎片了。
解决的方案一般如下:
- 如果你使用的是 Redis 4.0 以下版本,只能通过重启实例来解决
- 如果你使用的是 Redis 4.0 版本,它正好提供了自动碎片整理的功能,可以通过配置开启碎片自动整理
但是,开启内存碎片整理,它也有可能会导致 Redis 性能下降。原因在于,Redis 的碎片整理工作是也在主线程中执行的,当其进行碎片整理时,必然会消耗 CPU 资源,产生更多的耗时,从而影响到客户端的请求。所以,当你需要开启这个功能时,最好提前测试评估它对 Redis 的影响。
Redis 碎片整理的参数配置如下:
# 开启自动内存碎片整理(总开关)
activedefrag yes
# 内存使用 100MB 以下,不进行碎片整理
active-defrag-ignore-bytes 100mb
# 内存碎片率超过 10%,开始碎片整理
active-defrag-threshold-lower 10
# 内存碎片率超过 100%,尽最大努力碎片整理
active-defrag-threshold-upper 100
# 内存碎片整理占用 CPU 资源最小百分比
active-defrag-cycle-min 1
# 内存碎片整理占用 CPU 资源最大百分比
active-defrag-cycle-max 25
# 碎片整理期间,对于 List/Set/Hash/ZSet 类型元素一次 Scan 的数量
active-defrag-max-scan-fields 1000
4、使用Swap
操作系统为了缓解内存不足对应用程序的影响,允许把一部分内存中的数据换到磁盘上,以达到应用程序对内存使用的缓冲,这些内存数据被换到磁盘上的区域,就是 Swap。
当内存中的数据被换到磁盘上后,Redis 再访问这些数据时,就需要从磁盘上读取,访问磁盘的速度要比访问内存慢几百倍。尤其是针对 Redis 这种对性能要求极高、性能极其敏感的数据库来说,这个操作延时是无法接受的。
你需要检查 Redis 机器的内存使用情况,确认是否存在使用了 Swap。你可以通过以下方式来查看 Redis 进程是否使用到了 Swap。
# 先找到 Redis 的进程 ID
$ ps -aux | grep redis-server
# 查看 Redis Swap 使用情况
$ cat /proc/$pid/smaps | egrep '^(Swap|Size)'
这个结果会列出 Redis 进程的内存使用情况。每一行 Size 表示 Redis 所用的一块内存大小,Size 下面的 Swap 就表示这块 Size 大小的内存,有多少数据已经被换到磁盘上了,如果这两个值相等,说明这块内存的数据都已经完全被换到磁盘上了。
如果只是少量数据被换到磁盘上,例如每一块 Swap 占对应 Size 的比例很小,那影响并不是很大。如果是几百兆甚至上 GB 的内存被换到了磁盘上,那么你就需要警惕了,这种情况 Redis 的性能肯定会急剧下降。此时的解决方案是:
- 增加机器的内存,让 Redis 有足够的内存可以使用
- 整理内存空间,释放出足够的内存供 Redis 使用,然后释放 Redis 的 Swap,让 Redis 重新使用内存
释放 Redis 的 Swap 过程通常要重启实例,为了避免重启实例对业务的影响,一般会先进行主从切换,然后释放旧主节点的 Swap,重启旧主节点实例,待从库数据同步完成后,再进行主从切换即可。
总结
Redis 的性能问题,涉及到的知识点非常广,几乎涵盖了 CPU、内存、网络、甚至磁盘的方方面面,同时,你还需要了解计算机的体系结构,以及操作系统的各种机制。
从资源使用角度来看,包含的知识点如下:
- CPU 相关:使用复杂度过高命令、数据的持久化,都与耗费过多的 CPU 资源有关
- 内存相关:bigkey 内存的申请和释放、数据过期、数据淘汰、碎片整理、内存大页、内存写时复制都与内存息息相关
- 磁盘相关:数据持久化、AOF 刷盘策略,也会受到磁盘的影响
- 计算机网络:短连接、实例流量过载、网络流量过载,也会降低 Redis 性能
- 操作系统:写时复制、内存大页、Swap、CPU 绑定,都属于操作系统层面的知识