Redis变慢了(六) - 绑定CPU-开启AOF
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis变慢了(六) - 绑定CPU-开启AOF相关的知识,希望对你有一定的参考价值。
参考技术A很多时候,我们在部署服务时,为了提高性能,降低程序在使用多个CPU时上下文切换的性能损耗,一般会采用进程绑定CPU的操作。
但在使用Redis时,我们不建议这么干,原因如下。
绑定CPU的Redis,在进行数据持久化时,fork出的子进程,子进程会继承父进程的CPU使用偏好,而此时子进程会消耗大量的CPU资源进行数据持久化,子进程会与主进程发生CPU争抢,这也会导致主进程的CPU资源不足访问延迟增大。
所以在部署Redis进程时,如果需要开启RDB和AOF重写机制,一定不能进行CPU绑定操作!
上面提到了,当执行AOF文件重写时会因为fork执行耗时导致Redis延迟增大,除了这个之外,如果开启AOF机制,设置的策略不合理,也会导致性能问题。
开启AOF后,Redis会把写入的命令实时写入到文件中,但写入文件的过程是先写入内存,等内存中的数据超过一定阈值或达到一定时间后,内存中的内容才会被真正写入到磁盘中。
AOF为了保证文件写入磁盘的安全性,提供了3种刷盘机制:
当使用第一种机制appendfsync always时,Redis每处理一次写命令,都会把这个命令写入磁盘,而且这个操作是在主线程中执行的。
内存中的的数据写入磁盘,这个会加重磁盘的IO负担,操作磁盘成本要比操作内存的代价大得多。如果写入量很大,那么每次更新都会写入磁盘,此时机器的磁盘IO就会非常高,拖慢Redis的性能,因此我们不建议使用这种机制。
与第一种机制对比,appendfsync everysec会每隔1秒刷盘,而appendfsync no取决于操作系统的刷盘时间,安全性不高。因此我们推荐使用appendfsync everysec这种方式,在最坏的情况下,只会丢失1秒的数据,但它能保持较好的访问性能。
当然,对于有些业务场景,对丢失数据并不敏感,也可以不开启AOF。
你的Redis为什么变慢了?
往期热门文章:
1、
2、
3、
4
5
5
使用复杂度高的命令
# 命令执行超过5毫秒记录慢日志
CONFIG SET slowlog-log-slower-than 5000
# 只保留最近1000条慢日志
CONFIG SET slowlog-max-len 1000
SLOWLOG get 5
查询最近5条慢日志:
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) "book_price_1000"
...
O(n)
以上复杂度的命令,例如
sort
、
sunion
、
zunionstore
,或者在执行
O(n)
命令时操作的数据量比较大,这些情况下Redis处理数据时就会很耗时。
存储大key
SET
、
DELETE
操作出现在慢日志记录中,那么你就要怀疑是否存在Redis写入了大key的情况。
redis-cli -h $host -p $port --bigkeys -i 0.01
-i
参数控制即可,它表示扫描过程中每次扫描的时间间隔,单位是秒。
scan
命令,遍历所有key,然后针对不同类型的key执行
strlen
、
llen
、
hlen
、
scard
、
zcard
来获取字符串的长度以及容器类型(list/dict/set/zset)的元素个数。
lazy-free
的机制,用于异步释放大key的内存,降低对Redis性能的影响。即使这样,我们也不建议使用大key,大key在集群的迁移过程中,也会影响到迁移的性能,这个后面在介绍集群相关的文章时,会再详细介绍到。
集中过期
-
主动过期:Redis内部维护一个定时任务,默认每隔100毫秒会从过期字典中随机取出20个key,删除过期的key,如果过期key的比例超过了25%,则继续获取20个key,删除过期的key,循环往复,直到过期key的比例下降到25%或者这次任务的执行耗时超过了25毫秒,才会退出循环 -
懒惰过期:只有当访问某个key时,才判断这个key是否已过期,如果已经过期,则从实例中删除
expireat
或
pexpireat
命令,在代码中搜索这个关键字就可以了。
# 在过期时间点之后的5分钟内随机过期掉
redis.expireat(key, expire_time + random(300))
info
可以拿到所有的运行数据,在这里我们需要重点关注
expired_keys
这一项,它代表整个实例到目前为止,累计删除过期key的数量。
实例内存达到上限
maxmemory
,然后开启LRU淘汰策略。
maxmemory
后,你会发现之后的每次写入新的数据,有可能变慢了。
maxmemory
后,每次写入新的数据之前,必须先踢出一部分数据,让内存维持在
maxmemory
之下。
-
allkeys-lru:不管key是否设置了过期,淘汰最近最少访问的key -
volatile-lru:只淘汰最近最少访问并设置过期的key -
allkeys-random:不管key是否设置了过期,随机淘汰 -
volatile-random:只随机淘汰有设置过期的key -
allkeys-ttl:不管key是否设置了过期,淘汰即将过期的key -
noeviction:不淘汰任何key,满容后再写入直接报错 -
allkeys-lfu:不管key是否设置了过期,淘汰访问频率最低的key(4.0+支持) -
volatile-lfu:只淘汰访问频率最低的过期key(4.0+支持)
allkeys-lru
或
volatile-lru
策略,它们的处理逻辑是,每次从实例中随机取出一批key(可配置),然后淘汰一个最少访问的key,之后把剩下的key暂存到一个池子中,继续随机取出一批key,并与之前池子中的key比较,再淘汰一个最少访问的key。以此循环,直到内存降到
maxmemory
之下。
allkeys-random
或
volatile-random
策略,那么就会快很多,因为是随机淘汰,那么就少了比较key访问频率时间的消耗了,随机拿出一批key后直接淘汰即可,因此这个策略要比上面的LRU策略执行快一些。
maxmemory
限制实例的内存上限,同时面临淘汰key导致延迟增大的的情况,要想缓解这种情况,除了上面说的避免存储大key、使用随机淘汰策略之外,也可以考虑
拆分实例的方法来缓解,拆分实例可以把一个实例淘汰key的压力
分摊到多个实例上,可以在一定程度降低延迟。
fork耗时严重
fork
出一个子进程进行数据的持久化,
在fork
执行过程中,父进程需要拷贝内存页表给子进程,如果整个实例内存占用很大,那么需要拷贝的内存页表会比较耗时,此过程会消耗大量的CPU资源,在完成fork
之前,整个实例会被阻塞住,无法处理任何请求,如果此时CPU资源紧张,那么fork
的时间会更长,甚至达到秒级。这会严重影响Redis的性能。
info
命令,查看最后一次
fork
执行的耗时
latest_fork_usec
,单位微妙。这个时间就是整个实例阻塞无法处理请求的时间。
fork
的耗时也与系统有关,如果把Redis部署在虚拟机上,那么这个时间也会增大。所以使用Redis时建议部署在物理机上,降低
fork
的影响。
绑定CPU
fork
出的子进程,子进程会继承父进程的CPU使用偏好,而此时子进程会消耗大量的CPU资源进行数据持久化,子进程会与主进程发生CPU争抢,这也会导致主进程的CPU资源不足访问延迟增大。
开启AOF
fork
执行耗时导致Redis延迟增大,除了这个之外,如果开启AOF机制,设置的策略不合理,也会导致性能问题。
-
appendfsync always
:每次写入都刷盘,对性能影响最大,占用磁盘IO比较高,数据安全性最高 -
appendfsync everysec
:1秒刷一次盘,对性能影响相对较小,节点宕机时最多丢失1秒的数据 -
appendfsync no
:按照操作系统的机制刷盘,对性能影响最小,数据安全性低,节点宕机丢失数据取决于操作系统刷盘机制
appendfsync always
时,Redis每处理一次写命令,都会把这个命令写入磁盘,而且
这个操作是在主线程中执行的。
appendfsync everysec
会每隔1秒刷盘,而
appendfsync no
取决于操作系统的刷盘时间,安全性不高。因此我们推荐使用
appendfsync everysec
这种方式,在最坏的情况下,只会丢失1秒的数据,但它能保持较好的访问性能。
使用Swap
网卡负载过高
总结
fork
原理、Swap机制等,并对Redis的容量进行合理规划,预留足够的机器资源,对机器做好完善的监控,才能保证Redis的稳定运行。
往期热门文章:
1、
2、 5 6、
7 8 9
以上是关于Redis变慢了(六) - 绑定CPU-开启AOF的主要内容,如果未能解决你的问题,请参考以下文章