目录
首先我们要通过一些手段,分析定位是否是 Redis 问题
1. 应用程序作时间跟踪,比如服务链路、针对性时间日志等等,首先明确是 Redis 环节问题
2. 排除应用程序到 Redis 主机之间的网络延迟和丢包的现象
比如从应用程序到 Redis 主机作 ping 测试
3. 排出主机上的其它程序,操作系统配置对 Redis 的影响
比如在 Numa 架构下,Redis 进程和网卡被分配到不同的物理 CPU 上
4. 通过基准测试,进行一步分析 Redis 性能问题
5. 如果是集群的情况下,一般需要单个排除
我们可以使用以下命令,查看一段时间内 Redis 的最小、最大、平均访问延迟
redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1min --采用实时查看
LATENCY HISTOGRAM --查看历史统计数据
通过 Redis 的慢日志(slowlog)进行分析定位
1. 查看 Redis 慢日志之前,我们需要设置慢日志的阈值。例如,设置慢日志的阈值为 10000 微秒,并且保留最近 128 条慢日志记录
slowlog-log-slower-than 10000
slowlog-max-len 128
2. 这里我们可以通过执行命令:SLOWLOG get 50 进行查看以慢日志,进而分析原因
1) 唯一标志
2) 时间戳
3) 耗时(微秒)
4) 具体的命令
3. Redis 复杂度过高,一般是操作的内存数据过于复杂,或者是返回客户端的数据量比较大
比如像 SORT、SUNION、KEYS *、MGET、MSET 等命令,极有可能导致 Redis 变慢
如果一个 key 写入的 value 非常大,那么 Redis 在分配内存时就会比较耗时。同样的,当删除这个 key 时,释放内存也会比较耗时,这种类
型的 key 我们一般称之为 bigkey
Redis 中的 Key,原则上越小越好,一般超过 10K 时,Redis 性能会急剧下降,我们要特别小心
1. Redis 提供了扫描 bigkey 的命令,执行以下命令就可以扫描出,一个实例中 bigkey 的 top1 分布情况
redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.1
2. 针对 top N 的统计,我们可以采用 memory usage a 来进行统计
127.0.0.1:6379> memory usage a
(integer) 496
性能建议:
1. 强烈建议不写 bigkey
2. 开启惰性释放机制
lazyfree-lazy-user-del yes
lazyfree-lazy-server-del yes
replica-lazy-flush yes
Redis 的过期数据采用被动过期 + 主动过期两种策略:
1. 被动过期:只有当访问某个 key 时,才判断这个 key 是否已过期,如果已过期,则从实例中删除
2. 主动过期:Redis 内部维护了一个定时任务,默认每隔 100 毫秒(1 秒 10 次)就会从全局的过期哈希表中随机取出 20 个 key,然后删除其中过期的 key,如果过期 key 的比例超过了 25%,则继续重复此过程,直到过期 key 的比例下降到 25% 以下,或者这次任务的执行耗时超过
了 25 毫秒,才会退出循环
3. 如果对于 Key 过期出现过度集中问题,那么 Redis 的 CPU 会出现较大的波大
性能建议:
1. 集中过期 key 增加一个随机过期时间,把集中过期的时间打散,降低 Redis 清理过期 key 的压力
2. 如果是 Redis 是 4.0 以上版本,可以开启 lazy-free 机制,当删除过期 key 时,把释放内存的操作放到后台线程中执行,避免阻塞主线程
lazyfree-lazy-expire yes
当我们把 Redis 当做纯缓存使用时,通常会给这个实例设置一个内存上限 maxmemory,然后设置一个数据淘汰策略;如果 Redis 实例设置了
内存上限 maxmemory,那么也有可能导致 Redis 变慢
原因在于,当 Redis 内存达到 maxmemory 后,每次写入新的数据之前,Redis 必须先从实例中踢出一部分数据,让整个实例的内存维持在
maxmemory 之下,然后才能把新数据写进来。
这个踢出旧数据的逻辑也是需要消耗时间的,而具体耗时的长短,要取决于我们配置的淘汰策略
性能建议:
1. 避免存储 bigkey
2. 降低释放内存的耗时淘汰策略改为随机淘汰,随机淘汰比 LRU 要快很多
3. 开启憜性淘汰,lazyfree-lazy-eviction = yes
当 Redis 开启了后台 RDB 和 AOF rewrite 后,在执行时,它们都需要主进程创建出一个子进程进行数据的持久化
主进程创建子进程,会调用操作系统提供的 fork 函数;而 fork 在执行过程中,主进程需要拷贝自己的内存页表给子进程,如果这个实例很大,
那么这个拷贝的过程也会比较耗时
通过命令,我们可以查看上一次 fork 消耗的时间
redis-cli info|grep latest_fork_usec
性能建议:
1. 控制 Redis 实例的内存:尽量在 20G 以下,理论上越小越好,执行 fork 的耗时与实例大小有关,实例越大,耗时越久
2. 合理配置数据持久化策略:在 slave 节点执行 RDB 备份,推荐在低峰期执行,而对于丢失数据不敏感的业务(例如把 Redis 当做纯缓存使用),
可以关闭 AOF 和 AOF rewrite
3. Redis 实例不要部署在虚拟机上:fork 的耗时也与系统也有关,虚拟机比物理机耗时更久
4. 降低主从库全量同步的概率:适当调大 repl-backlog-size 参数,避免主从全量同步
我们都知道,应用程序向操作系统申请内存时,是按内存页进行申请的,而常规的内存页大小是 4KB。
Linux 内核从 2.6.38 开始,支持了内存大页机制,该机制允许应用程序以 2MB 大小为单位,向操作系统申请内存
应用程序每次向操作系统申请的内存单位变大了,但这也意味着申请内存的耗时变长
但是对于 Redis 这种对性能和延迟极其敏感的数据库来说,我们希望 Redis 在每次申请内存时,耗时尽量短,所以我不建议你在 Redis 机器上
开启这个机制。
性能建议:
1、查看 Redis 机器是否开启了内存大页: #如果输出选项是 always,就表示目前开启了内存大页机制
cat /sys/kernel/mm/transparent_hugepage/enabled [always] madvise never
2、关闭大内存页
echo never > /sys/kernel/mm/transparent_hugepage/enabled
AOF 刷盘不同模式对性能影响是不一样
性能建议:
1. 如果是热点场景,建议大家关闭 rdb 和 aof
2. 在 SATA 和 SAS 普通盘上,appendfsync=always 和 appendfsync=everysec 性能差异基本在 50 倍以上
3. 在 aof 开启而且刷盘方式 appendfsync=always 时,磁盘对 Redis 的写入性能影响非常大,通常我们只在 SSD 时才尝试开启
4. 如果主机 IO 非常繁忙,appendfsync=everysec 在极限情况下可能导致 Redis 主线程堵塞
很多时候,我们在部署服务时,为了提高服务性能,降低应用程序在多个 CPU 核心之间的上下文切换带来的性能损耗,通常采用的方案是进
程绑定 CPU 的方式提高性能
Redis6.0 以前,绑定 CPU 也会带来风险,如果我们把 Redis 进程只绑定了一个 CPU 逻辑核心上,那么当 Redis 在进行数据持久化时,fork 出 的子进程会继承父进程的 CPU 使用偏好。
而此时的子进程会消耗大量的 CPU 资源进行数据持久化(把实例数据全部扫描出来需要耗费 CPU),这就会导致子进程会与主进程发生 CPU 争抢,进而影响到主进程服务客户端请求,访问延迟变大
Redis 在 6.0 版本已经推出了这个功能,我们可以通过以下配置,对主线程、后台线程、后台 RDB 进程、AOF rewrite 进程,绑定固定的 CPU逻辑核心
性能建议
1. Redis Server 和 IO 线程绑定到 CPU 核心 0,2,4,6:
server_cpulist 0-7:2
2. 后台子线程绑定到 CPU 核心 1,3:
bio_cpulist 1,3
3. 后台 AOF rewrite 进程绑定到 CPU 核心 8,9,10,11:
aof_rewrite_cpulist 8-11
4. 后台 RDB 进程绑定到 CPU 核心 1,10,11:
bgsave_cpulist 1,10-1
在 Numa 架构下,Redis 绑定的 CPU 要在同一个物理 CPU 下,而且必须和网卡绑定在同一个物理 CPU 下
Redis 的数据都存储在内存中,当我们的应用程序频繁修改 Redis 中的数据时,就有可能会导致 Redis 产生内存碎片
内存碎片会降低 Redis 的内存使用率,我们可以通过执行 INFO 命令,得到这个实例的内存碎片率
mem_fragmentation_ratio = used_memory_rss / used_memory
其中 used_memory 表示 Redis 存储数据的内存大小,而 used_memory_rss 表示操作系统实际分配给 Redis 进程的大小
如果 mem_fragmentation_ratio > 2,说明内存碎片率已经超过了 100%,这时我们就需要采取一些措施来降低内存碎片了
性能建议:
如果是 Redis 4.0 以下版本,只能通过重启实例来解决
如果是 Redis 4.0 +版本,正好提供了自动碎片整理的功能,可以通过配置开启碎片自动整理
activedefrag yes #开启自动内存碎片整理
active-defrag-ignore-bytes 100mb #内存使用 100MB 以下,不进行碎片整理
active-defrag-threshold-lower 50 #内存碎片率超过 50%,开始碎片整理
active-defrag-threshold-upper 100 #内存碎片率超过 100%,尽最大努力碎片整理
active-defrag-cycle-min 1 #内存碎片整理占用 CPU 资源最小百分比
active-defrag-cycle-max 25 #内存碎片整理占用 CPU 资源最大百分比
active-defrag-max-scan-fields 1000 #碎片整理期间,对于 List/Set/Hash/ZSet 类型元素一次 Scan 的数量