首页 » 科学 » 你的Redis真的变慢了吗?机能优化若何做_内存_数据

你的Redis真的变慢了吗?机能优化若何做_内存_数据

雨夜梧桐 2025-01-05 04:28:10 0

扫一扫用手机浏览

文章目录 [+]

阿里妹导读

本文先讲述了Redis变慢的判别方法,后面讲述了如何提升性能。

你的Redis真的变慢了吗?机能优化若何做_内存_数据 你的Redis真的变慢了吗?机能优化若何做_内存_数据 科学

一、Redis为什么变慢了

你的Redis真的变慢了吗?机能优化若何做_内存_数据 你的Redis真的变慢了吗?机能优化若何做_内存_数据 科学
(图片来自网络侵删)

1.Redis真的变慢了吗?

对 Redis 进行基准性能测试

例如,我的机器配置比较低,当延迟为 2ms 时,我就认为 Redis 变慢了,但是如果你的硬件配置比较高,那么在你的运行环境下,可能延迟是 0.5ms 时就可以认为 Redis 变慢了。

以是,你只有理解了你的 Redis 在生产环境做事器上的基准性能,才能进一步评估,当其延迟达到什么程度时,才认为 Redis 确实变慢了。

为了避免业务做事器到 Redis 做事器之间的网络延迟,你须要直接在 Redis 做事器上测试实例的相应延迟情形。
实行以下命令,就可以测试出这个实例 60 秒内的最大相应延迟:

./redis-cli --intrinsic-latency 120Max latency so far: 17 microseconds.Max latency so far: 44 microseconds.Max latency so far: 94 microseconds.Max latency so far: 110 microseconds.Max latency so far: 119 microseconds.36481658 total runs (avg latency: 3.2893 microseconds / 3289.32 nanoseconds per run).Worst run took 36x longer than the average latency.

从输出结果可以看到,这 60 秒内的最大相应延迟为 119 微秒(0.119毫秒)。
你还可以利用以下命令,查看一段韶光内 Redis 的最小、最大、均匀访问延迟

$ redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1min: 0, max: 1, avg: 0.13 (100 samples) -- 1.01 seconds rangemin: 0, max: 1, avg: 0.12 (99 samples) -- 1.01 seconds rangemin: 0, max: 1, avg: 0.13 (99 samples) -- 1.01 seconds rangemin: 0, max: 1, avg: 0.10 (99 samples) -- 1.01 seconds rangemin: 0, max: 1, avg: 0.13 (98 samples) -- 1.00 seconds rangemin: 0, max: 1, avg: 0.08 (99 samples) -- 1.01 seconds range

如果你不雅观察到的 Redis 运行时延迟是其基线性能的 2 倍及以上,就可以认定 Redis 变慢了。


网络对 Redis 性能的影响,一个大略的方法是用 iPerf 这样的工具测试网络极限带宽。

做事器端# iperf -s -p 12345 -i 1 -Miperf: option requires an argument -- M------------------------------------------------------------Server listening on TCP port 12345TCP window size: 4.00 MByte (default)------------------------------------------------------------[ 4] local 172.20.0.113 port 12345 connected with 172.20.0.114 port 56796[ ID] Interval Transfer Bandwidth[ 4] 0.0- 1.0 sec 614 MBytes 5.15 Gbits/sec[ 4] 1.0- 2.0 sec 622 MBytes 5.21 Gbits/sec[ 4] 2.0- 3.0 sec 647 MBytes 5.42 Gbits/sec[ 4] 3.0- 4.0 sec 644 MBytes 5.40 Gbits/sec[ 4] 4.0- 5.0 sec 651 MBytes 5.46 Gbits/sec[ 4] 5.0- 6.0 sec 652 MBytes 5.47 Gbits/sec[ 4] 6.0- 7.0 sec 669 MBytes 5.61 Gbits/sec[ 4] 7.0- 8.0 sec 670 MBytes 5.62 Gbits/sec[ 4] 8.0- 9.0 sec 667 MBytes 5.59 Gbits/sec[ 4] 9.0-10.0 sec 667 MBytes 5.60 Gbits/sec[ 4] 0.0-10.0 sec 6.35 GBytes 5.45 Gbits/sec客户端# iperf -c 做事器端IP -p 12345 -i 1 -t 10 -w 20K------------------------------------------------------------Client connecting to 172.20.0.113, TCP port 12345TCP window size: 40.0 KByte (WARNING: requested 20.0 KByte)------------------------------------------------------------[ 3] local 172.20.0.114 port 56796 connected with 172.20.0.113 port 12345[ ID] Interval Transfer Bandwidth[ 3] 0.0- 1.0 sec 614 MBytes 5.15 Gbits/sec[ 3] 1.0- 2.0 sec 622 MBytes 5.21 Gbits/sec[ 3] 2.0- 3.0 sec 646 MBytes 5.42 Gbits/sec[ 3] 3.0- 4.0 sec 644 MBytes 5.40 Gbits/sec[ 3] 4.0- 5.0 sec 651 MBytes 5.46 Gbits/sec[ 3] 5.0- 6.0 sec 652 MBytes 5.47 Gbits/sec[ 3] 6.0- 7.0 sec 669 MBytes 5.61 Gbits/sec[ 3] 7.0- 8.0 sec 670 MBytes 5.62 Gbits/sec[ 3] 8.0- 9.0 sec 667 MBytes 5.59 Gbits/sec[ 3] 9.0-10.0 sec 668 MBytes 5.60 Gbits/sec[ 3] 0.0-10.0 sec 6.35 GBytes 5.45 Gbits/sec

2.利用繁芜度过高的命令

首先,第一步,你须要去查看一下 Redis 的慢日志(slowlog)。

Redis 供应了慢日志命令的统计功能,它记录了有哪些命令在实行时耗时比较久。

查看 Redis 慢日志之前,你须要设置慢日志的阈值。
例如,设置慢日志的阈值为 5 毫秒,并且保留最近 500 条慢日志记录:

# 命令实行耗时超过 5 毫秒,记录慢日志CONFIG SET slowlog-log-slower-than 5000# 只保留最近 500 条慢日志CONFIG SET slowlog-max-len 500

1.常常利用 O(N) 以上繁芜度的命令,例如 SORT、SUNION、ZUNIONSTORE 聚合类命令

2.利用 O(N) 繁芜度的命令,但 N 的值非常大

第一种情形导致变慢的缘故原由在于,Redis 在操作内存数据时,韶光繁芜度过高,要花费更多的 CPU 资源。

第二种情形导致变慢的缘故原由在于,Redis 一次须要返回给客户真个数据过多,更多韶光花费在数据协议的组装和网络传输过程中。

其余,我们还可以从资源利用率层面来剖析,如果你的运用程序操作 Redis 的 OPS 不是很大,但 Redis 实例的 CPU 利用率却很高,那么很有可能是利用了繁芜度过高的命令导致的。

3.操作bigkey

如果你查询慢日志创造,并不是繁芜度过高的命令导致的,而都是 SET / DEL 这种大略命令涌如今慢日志中,那么你就要疑惑你的实例否写入了 bigkey。

redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 1-------- 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 bytesBiggest list found 'mylist:004' has 40 itemsBiggest set found 'myset:2386' has 38 membersBiggest hash found 'myhash:3574' has 37 fieldsBiggest zset found 'myzset:2704' has 42 members36313 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)

这里我须要提醒你的是,当实行这个命令时,要把稳 2 个问题:

1.对线上实例进行 bigkey 扫描时,Redis 的 OPS 会突增,为了降落扫描过程中对 Redis 的影响,最好掌握一下扫描的频率,指定 -i 参数即可,它表示扫描过程中每次扫描后安歇的韶光间隔,单位是秒

2.扫描结果中,对付容器类型(List、Hash、Set、ZSet)的 key,只能扫描出元素最多的 key。
但一个 key 的元素多,不一定表示占用内存也多,你还须要根据业务情形,进一步评估内存占用情形。

4.集中过期

如果你创造,平时在操作 Redis 时,并没有延迟很大的情形发生,但在某个韶光点溘然涌现一波延时,其征象表现为:变慢的韶光点很有规律,例如某个整点,或者每间隔多久就会发生一波延迟。

如果是涌现这种情形,那么你须要排查一下,业务代码中是否存在设置大量 key 集中过期的情形。

如果有大量的 key 在某个固定时间点集中过期,在这个韶光点访问 Redis 时,就有可能导致延时变大。

Redis 的过期数据采取被动过期 + 主动过期两种策略:

1.被动过期:只有当访问某个 key 时,才判断这个 key 是否已过期,如果已过期,则从实例中删除

2.主动过期:Redis 内部掩护了一个定时任务,默认每隔 100 毫秒(1秒10次)就会从全局的过期哈希表中随机取出 20 个 key,然后删除个中过期的 key,如果过期 key 的比例超过了 25%,则连续重复此过程,直到过期 key 的比例低落到 25% 以下,或者这次任务的实行耗时超过了 25 毫秒,才会退出循环

把稳,这个主动过期 key 的定时任务,是在 Redis 主线程中实行的。

也便是说如果在实行主动过期的过程中,涌现了须要大量删除过期 key 的情形,那么此时运用程序在访问 Redis 时,必须要等待这个过期任务实行结束,Redis 才可以做事这个客户端要求。

如果此时须要过期删除的是一个 bigkey,那么这个耗时会更久。
而且,这个操作延迟的命令并不会记录在慢日志中。

由于慢日志中只记录一个命令真正操作内存数据的耗时,而 Redis 主动删除过期 key 的逻辑,是在命令真正实行之前实行的。

5.实例内存达到上限

当我们把 Redis 当做纯缓存利用时,常日会给这个实例设置一个内存上限 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+版本支持)

一样平常最常利用的是 allkeys-lru / volatile-lru 淘汰策略,它们的处理逻辑是,每次从实例中随机取出一批 key(这个数量可配置),然后淘汰一个最少访问的 key,之后把剩下的 key 暂存到一个池子中,连续随机取一批 key,并与之前池子中的 key 比较,再淘汰一个最少访问的 key。
以此往来来往,直到实例内存降到 maxmemory 之下。

须要把稳的是,Redis 的淘汰数据的逻辑与删除过期 key 的一样,也是在命令真正实行之前实行的,也便是说它也会增加我们操作 Redis 的延迟,而且,写 OPS 越高,延迟也会越明显。

如果此时你的 Redis 实例中还存储了 bigkey,那么在淘汰删除 bigkey 开释内存时,也会耗时比较久。

6.fork耗时严重

当 Redis 开启了后台 RDB 和 AOF rewrite 后,在实行时,它们都须要主进程创建出一个子进程进行数据的持久化。

主进程创建子进程,会调用操作系统供应的 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 产生性能影响。

7.开启内存大页

除了上面讲到的子进程 RDB 和 AOF rewrite 期间,fork 耗时导致的延时变大之外,这里还有一个方面也会导致性能问题,这便是操作系统是否开启了内存大页机制。

什么是内存大页?

我们都知道,运用程序向操作系统申请内存时,是按内存页进行申请的,而常规的内存页大小是 4KB。

Linux 内核从 2.6.38 开始,支持了内存大页机制,该机制许可运用程序以 2MB 大小为单位,向操作系统申请内存。

运用程序每次向操作系统申请的内存单位变大了,但这也意味着申请内存的耗时变长。

这对 Redis 会有什么影响呢?

当 Redis 在实行后台 RDB,采取 fork 子进程的办法来处理。
但主进程 fork 子进程后,此时的主进程依旧是可以吸收写要求的,而进来的写要求,会采取 Copy On Write(写时复制)的办法操作内存数据。

也便是说,主进程一旦有数据须要修正,Redis 并不会直接修正现有内存中的数据,而是先将这块内存数据拷贝出来,再修正这块新内存的数据,这便是所谓的「写时复制」。

写时复制你也可以理解成,谁须要发生写操作,谁就须要先拷贝,再修正。

这样做的好处是,父进程有任何写操作,并不会影响子进程的数据持久化(子进程只持久化 fork 这一瞬间全体实例中的所有数据即可,不关心新的数据变更,由于子进程只须要一份内存快照,然后持久化到磁盘上)。

但是请把稳,主进程在拷贝内存数据时,这个阶段就涉及到新内存的申请,如果此时操作系统开启了内存大页,那么在此期间,客户端即便只修正 10B 的数据,Redis 在申请内存时也会以 2MB 为单位向操作系统申请,申请内存的耗时变长,进而导致每个写要求的延迟增加,影响到 Redis 性能。

同样地,如果这个写要求操作的是一个 bigkey,那主进程在拷贝这个 bigkey 内存块时,一次申请的内存会更大,韶光也会更久。
可见,bigkey 在这里又一次影响到了性能。

8.开启AOF

前面我们剖析了 RDB 和 AOF rewrite 对 Redis 性能的影响,紧张关注点在 fork 上。

实在,关于数据持久化方面,还有影响 Redis 性能的成分,这次我们重点来看 AOF 数据持久化。

如果你的 AOF 配置不合理,还是有可能会导致性能问题。

当 Redis 开启 AOF 后,其事情事理如下:

1.Redis 实行写命令后,把这个命令写入到 AOF 文件内存中(write 系统调用)

2.Redis 根据配置的 AOF 刷盘策略,把 AOF 内存数据刷到磁盘上(fsync 系统调用)

为了担保 AOF 文件数据的安全性,Redis 供应了 3 种刷盘机制:

1.appendfsync always:主线程每次实行写操作后立即刷盘,此方案会占用比较大的磁盘 IO 资源,但数据安全性最高

2.appendfsync no:主线程每次写操作只写内存就返回,内存数据什么时候刷到磁盘,交由操作系统决定,此方案对性能影响最小,但数据安全性也最低,Redis 宕机时丢失的数据取决于操作系统刷盘机遇

3.appendfsync everysec:主线程每次写操作只写内存就返回,然后由后台线程每隔 1 秒实行一次刷盘操作(触发fsync系统调用),此方案对性能影响相对较小,但当 Redis 宕机时会丢失 1 秒的数据

看到这里,我猜你肯定和大多数人的想法一样,选比较折中的方案 appendfsync everysec 就没问题了吧?

这个方案上风在于,Redis 主线程写完内存后就返回,详细的刷盘操作是放到后台线程中实行的,后台线程每隔 1 秒把内存中的数据刷到磁盘中。

这种方案既兼顾了性能,又尽可能地担保了数据安全,是不是以为很完美?

但是,这里我要给你泼一盆冷水了,采取这种方案你也要当心一下,由于这种方案还是存在导致 Redis 延迟变大的情形发生,乃至会壅塞全体 Redis。

你试想这样一种情形:当 Redis 后台线程在实行 AOF 文件刷盘时,如果此时磁盘的 IO 负载很高,那这个后台线程在实行刷盘操作(fsync系统调用)时就会被壅塞住。

此时的主线程依旧会吸收写要求,紧接着,主线程又须要把数据写到文件内存中(write 系统调用),当主线程利用后台子线程实行了一次 fsync,须要再次把新吸收的操作记录写回磁盘时,如果主线程创造上一次的 fsync 还没有实行完,那么它就会壅塞。
以是,如果后台子线程实行的 fsync 频繁壅塞的话(比如 AOF 重写占用了大量的磁盘 IO 带宽),主线程也会壅塞,导致 Redis 性能变慢。

看到了么?在这个过程中,主线程依旧有壅塞的风险。

以是,只管你的 AOF 配置为 appendfsync everysec,也不能掉以轻心,要当心磁盘压力过大导致的 Redis 有性能问题。

那什么情形下会导致磁盘 IO 负载过大?以及如何办理这个问题呢?

我总结了以下几种情形,你可以参考进行问题排查:

1.子进程正在实行 AOF rewrite,这个过程会占用大量的磁盘 IO 资源

2.有其他运用程序在实行大量的写文件操作,也会占用磁盘 IO 资源

对付情形1,说白了便是,Redis 的 AOF 后台子线程刷盘操作,撞上了子进程 AOF rewrite!

9.绑定CPU

很多时候,我们在支配做事时,为了提高做事性能,降落运用程序在多个 CPU 核心之间的高下文切换带来的性能损耗,常日采取的方案是进程绑定 CPU 的办法提高性能。

我们都知道,一样平常当代的做事器会有多个 CPU,而每个 CPU 又包含多个物理核心,每个物理核心又分为多个逻辑核心,每个物理核下的逻辑核共用 L1/L2 Cache。

而 Redis Server 除了主线程做事客户端要求之外,还会创建子进程、子线程。

个中子进程用于数据持久化,而子线程用于实行一些比较耗时操作,例如异步开释 fd、异步 AOF 刷盘、异步 lazy-free 等等。

如果你把 Redis 进程只绑定了一个 CPU 逻辑核心上,那么当 Redis 在进行数据持久化时,fork 出的子进程会继续父进程的 CPU 利用偏好。

而此时的子进程会花费大量的 CPU 资源进行数据持久化(把实例数据全部扫描出来须要耗费CPU),这就会导致子进程会与主进程发生 CPU 争抢,进而影响到主进程做事客户端要求,访问延迟变大。

这便是 Redis 绑定 CPU 带来的性能问题。

10.利用Swap

如果你创造 Redis 溘然变得非常慢,每次的操作耗时都达到了几百毫秒乃至秒级,那此时你就须要检讨 Redis 是否利用到了 Swap,在这种情形下 Redis 基本上已经无法供应高性能的做事了。

什么是 Swap?为什么利用 Swap 会导致 Redis 的性能低落?

如果你对操作系统有些理解,就会知道操作系统为了缓解内存不敷对运用程序的影响,许可把一部分内存中的数据换到磁盘上,以达到运用程序对内存利用的缓冲,这些内存数据被换到磁盘上的区域,便是 Swap。

问题就在于,当内存中的数据被换到磁盘上后,Redis 再访问这些数据时,就须要从磁盘上读取,访问磁盘的速率要比访问内存慢几百倍!

尤其是针对 Redis 这种对性能哀求极高、性能极其敏感的数据库来说,这个操作延时是无法接管的。

此时,你须要检讨 Redis 机器的内存利用情形,确认是否存在利用了 Swap。

你可以通过以下办法来查看 Redis 进程是否利用到了 Swap:

# 先找到 Redis 的进程 ID$ ps -aux | grep redis-server# 查看 Redis Swap 利用情形$ cat /proc/$pid/smaps | egrep '^(Swap|Size)'

输出结果如下:

Size: 1256 kBSwap: 0 kBSize: 4 kBSwap: 0 kBSize: 132 kBSwap: 0 kBSize: 63488 kBSwap: 0 kBSize: 132 kBSwap: 0 kBSize: 65404 kBSwap: 0 kBSize: 1921024 kBSwap: 0 kB

每一行 Size 表示 Redis 所用的一块内存大小,Size 下面的 Swap 就表示这块 Size 大小的内存,有多少数据已经被换到磁盘上了,如果这两个值相等,解释这块内存的数据都已经完备被换到磁盘上了。

如果只是少量数据被换到磁盘上,例如每一块 Swap 占对应 Size 的比例很小,那影响并不是很大。
如果是几百兆乃至上 GB 的内存被换到了磁盘上,那么你就须要当心了,这种情形 Redis 的性能肯定会急剧低落。

11.碎片整理

Redis 的数据都存储在内存中,当我们的运用程序频繁修正 Redis 中的数据时,就有可能会导致 Redis 产生内存碎片。

内存碎片会降落 Redis 的内存利用率,我们可以通过实行 INFO 命令,得到这个实例的内存碎片率:

# Memoryused_memory:5709194824used_memory_human:5.32Gused_memory_rss:8264855552used_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%,这时我们就须要采纳一些方法来降落内存碎片了。

办理的方案一样平常如下:

1.如果你利用的是 Redis 4.0 以下版本,只能通过重启实例来办理

2.如果你利用的是 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

二、Redis如何优化

1.慢查询优化

1.只管即便不该用 O(N) 以上繁芜度过高的命令,对付数据的聚合操作,放在客户端做

2.实行 O(N) 命令,担保 N 只管即便的小(推举 N <= 300),每次获取只管即便少的数据,让 Redis 可以及时处理返回

2.集中过期优化

一样平常有两种方案来规避这个问题:

1.集中过期 key 增加一个随机过期韶光,把集中过期的韶光打散,降落 Redis 清理过期 key 的压力

2.如果你利用的 Redis 是 4.0 以上版本,可以开启 lazy-free 机制,当删除过期 key 时,把开释内存的操作放到后台线程中实行,避免壅塞主线程。

第一种方案,在设置 key 的过期韶光时,增加一个随机韶光,伪代码可以这么写:

# 在过期韶光点之后的 5 分钟内随机过期掉redis.expireat(key, expire_time + random(300))

第二种方案,Redis 4.0 以上版本,开启 lazy-free 机制:

# 开释过期 key 的内存,放到后台线程实行lazyfree-lazy-expire yes

运维层面,你须要把 Redis 的各项运行状态数据监控起来,在 Redis 上实行 INFO 命令就可以拿到这个实例所有的运行状态数据。

在这里我们须要重点关注 expired_keys 这一项,它代表全体实例到目前为止,累计删除过期 key 的数量。

你须要把这个指标监控起来,当这个指标在很短韶光内涌现了突增,须要及时报警出来,然后与业务运用报慢的韶光点进行比拟剖析,确认韶光是否同等,如果同等,则可以确认确实是由于集中过期 key 导致的延迟变大。

3.实例内存达到上限优化

1.避免存储 bigkey,降落开释内存的耗时

2.淘汰策略改为随机淘汰,随机淘汰比 LRU 要快很多(视业务情形调度)

3.拆分实例,把淘汰 key 的压力分摊到多个实例上

4.如果利用的是 Redis 4.0 以上版本,开启 layz-free 机制,把淘汰 key 开释内存的操作放到后台线程中实行(配置 lazyfree-lazy-eviction = yes)

4.fork耗时严重优化

1.掌握 Redis 实例的内存:只管即便在 10G 以下,实行 fork 的耗时与实例大小有关,实例越大,耗时越久。

2.合理配置数据持久化策略:在 slave 节点实行 RDB 备份,推举在低峰期实行,而对付丢失数据不敏感的业务(例如把 Redis 当做纯缓存利用),可以关闭 AOF 和 AOF rewrite。

3.Redis 实例不要支配在虚拟机上:fork 的耗时也与系统也有关,虚拟机比物理机耗时更久。

4.降落主从库全量同步的概率:适当调大 repl-backlog-size 参数,避免主从全量同步。

从建立同步时,优先检测是否可以考试测验只同步部分数据,这种情形便是针对付之前已经建立好了复制链路,只是由于故障导致临时断开,故障规复后重新建立同步时,为了避免全量同步的资源花费,Redis会优先考试测验部分数据同步,如果条件不符合,才会触发全量同步。
这个判断依据便是在master上掩护的复制缓冲区大小,如果这个缓冲区配置的过小,很有可能在主从断开复制的这段韶光内,master产生的写入导致复制缓冲区的数据被覆盖,重新建立同步时的slave须要同步的offset位置在master的缓冲区中找不到,那么此时就会触发全量同步。
如何避免这种情形?办理方案便是适当调大复制缓冲区repl-backlog-size的大小,这个缓冲区的大小默认为1MB,如果实例写入量比较大,可以针对性调大此配置。

5.多核CPU优化

那如何办理这个问题呢?

如果你确实想要绑定 CPU,可以优化的方案是,不要让 Redis 进程只绑定在一个 CPU 逻辑核上,而是绑定在多个逻辑核心上,而且,绑定的多个逻辑核心最好是同一个物理核心,这样它们还可以共用 L1/L2 Cache。

当然,即便我们把 Redis 绑定在多个逻辑核心上,也只能在一定程度上缓解主线程、子进程、后台线程在 CPU 资源上的竞争。

由于这些子进程、子线程还是会在这多个逻辑核心上进行切换,存在性能损耗。


如何再进一步优化?

可能你已经想到了,我们是否可以让主线程、子进程、后台线程,分别绑定在固定的 CPU 核心上,不让它们来回切换,这样一来,他们各自利用的 CPU 资源互不影响。

实在,这个方案 Redis 官方已经想到了。

Redis 在 6.0 版本已经推出了这个功能,我们可以通过以下配置,对主线程、后台线程、后台 RDB 进程、AOF rewrite 进程,绑定固定的 CPU 逻辑核心:

Redis6.0 前绑定CPU核

taskset -c 0 ./redis-server

Redis6.0 后绑定CPU核

# Redis Server 和 IO 线程绑定到 CPU核心 0,2,4,6server_cpulist 0-7:2# 后台子线程绑定到 CPU核心 1,3bio_cpulist 1,3# 后台 AOF rewrite 进程绑定到 CPU 核心 8,9,10,11aof_rewrite_cpulist 8-11# 后台 RDB 进程绑定到 CPU 核心 1,10,11# bgsave_cpulist 1,10-1

如果你利用的恰好是 Redis 6.0 版本,就可以通过以上配置,来进一步提高 Redis 性能。

这里我须要提醒你的是,一样平常来说,Redis 的性能已经足够精良,除非你对 Redis 的性能有更加严苛的哀求,否则不建议你绑定 CPU。

6.查看Redis内存是否发生Swap

$ redis-cli info | grep process_idprocess_id: 5332

然后,进入 Redis 所在机器的 /proc 目录下的该进程目录中:

$ cd /proc/5332

末了,运行下面的命令,查看该 Redis 进程的利用情形。
在这儿,我只截取了部分结果:

$cat smaps | egrep '^(Swap|Size)'Size: 584 kBSwap: 0 kBSize: 4 kBSwap: 4 kBSize: 4 kBSwap: 0 kBSize: 462044 kBSwap: 462008 kBSize: 21392 kBSwap: 0 kB

一旦发生内存 swap,最直接的办理方法便是增加机器内存。
如果该实例在一个 Redis 切片集群中,可以增加 Redis 集群的实例个数,来分摊每个实例做事的数据量,进而减少每个实例所需的内存量。

7.内存大页

如果采取了内存大页,那么,纵然客户端要求只修正 100B 的数据,Redis 也须要拷贝 2MB 的大页。
相反,如果是常规内存页机制,只用拷贝 4KB。
两者比较,你可以看到,当客户端要求修正或新写入数据较多时,内存大页机制将导致大量的拷贝,这就会影响 Redis 正常的访存操作,终极导致性能变慢。

首先,我们要先排查下内存大页。
方法是:在 Redis 实例运行的机器上实行如下命令:

$ cat /sys/kernel/mm/transparent_hugepage/enabled[always] madvise never

如果实行结果是 always,就表明内存大页机制被启动了;如果是 never,就表示,内存大页机制被禁止。

在实际生产环境中支配时,我建议你不要利用内存大页机制,操作也很大略,只须要实行下面的命令就可以了:

echo never /sys/kernel/mm/transparent_hugepage/enabled

实在,操作系统供应的内存大页机制,其上风是,可以在一定程序上降落运用程序申请内存的次数。

但是对付 Redis 这种对性能和延迟极其敏感的数据库来说,我们希望 Redis 在每次申请内存时,耗时只管即便短,以是我不建议你在 Redis 机器上开启这个机制。

8.删除利用Lazy Free

支持版本:Redis 4.0+

8.1 主动删除键利用lazy freeUNLINK命令

127.0.0.1:7000> LLEN mylist(integer) 2000000127.0.0.1:7000> UNLINK mylist(integer) 1127.0.0.1:7000> SLOWLOG get1) 1) (integer) 1 2) (integer) 1505465188 3) (integer) 30 4) 1) "UNLINK" 2) "mylist" 5) "127.0.0.1:17015" 6) ""

把稳:DEL命令,还是并发壅塞的删除操作

FLUSHALL/FLUSHDB ASYNC

127.0.0.1:7000> DBSIZE(integer) 1812295127.0.0.1:7000> flushall //同步清理实例数据,180万个key耗时1020毫秒OK(1.02s)127.0.0.1:7000> DBSIZE(integer) 1812637127.0.0.1:7000> flushall async //异步清理实例数据,180万个key耗时约9毫秒OK127.0.0.1:7000> SLOWLOG get 1) 1) (integer) 2996109 2) (integer) 1505465989 3) (integer) 9274 //指令运行耗时9.2毫秒 4) 1) "flushall" 2) "async" 5) "127.0.0.1:20110" 6) ""

8.2 被动删除键利用lazy free

lazy free运用于被动删除中,目前有4种场景,每种场景对应一个配置参数;默认都是关闭。

lazyfree-lazy-eviction nolazyfree-lazy-expire nolazyfree-lazy-server-del noslave-lazy-flush no

lazyfree-lazy-eviction

针对redis内存利用达到maxmeory,并设置有淘汰策略时;在被动淘汰键时,是否采取lazy free机制;由于此场景开启lazy free, 可能利用淘汰键的内存开释不及时,导致redis内存超用,超过maxmemory的限定。
此场景利用时,请结合业务测试。
(生产环境不建议设置yes)

lazyfree-lazy-expire

针对设置有TTL的键,达到过期后,被redis清理删除时是否采取lazy free机制;此场景建议开启,因TTL本身是自适应调度的速率。

lazyfree-lazy-server-del

针对有些指令在处理已存在的键时,会带有一个隐式的DEL键的操作。
如rename命令,当目标键已存在,redis会先删除目标键,如果这些目标键是一个big key,那就会引入壅塞删除的性能问题。
此参数设置便是办理这类问题,建议可开启。

slave-lazy-flush

针对slave进行全量数据同步,slave在加载master的RDB文件前,会运行flushall来清理自己的数据场景, 参数设置决定是否采取非常flush机制。
如果内存变动不大,建议可开启。
可减少全量同步耗时,从而减少主库因输出缓冲区爆涨引起的内存利用增长。

8.3 lazy free的监控

lazy free能监控的数据指标,只有一个值:lazyfree_pending_objects,表示redis实行lazy free操作,在等待被实际回收内容的键个数。
并不能表示单个大键的元素个数或等待lazy free回收的内存大小。
以是此值有一定参考值,可监测redis lazy free的效率或堆积键数量;比如在flushall async场景下会有少量的堆积。

# info memory# Memorylazyfree_pending_objects:0

把稳事变:unlink命令入口函数unlinkCommand()和del调用相同函数delGenericCommand()进行删除KEY操作,利用lazy标识是否为lazyfree调用。
如果是lazyfree,则调用dbAsyncDelete()函数。

但并非每次unlink命令就一定启用lazy free,redis会先判断开释KEY的代价(cost),当cost大于LAZYFREE_THRESHOLD(64)才进行lazy free.

开释key代价打算函数lazyfreeGetFreeEffort(),凑集类型键,且知足对应编码,cost便是凑集键的元数个数,否则cost便是1.

举例:

1 一个包含100元素的list key, 它的free cost便是100

2 一个512MB的string key, 它的free cost是1 以是可以看出,redis的lazy free的cost打算紧张韶光繁芜度干系。

9.AOF优化

Redis 供应了一个配置项,当子进程在 AOF rewrite 期间,可以让后台子线程不实行刷盘(不触发 fsync 系统调用)操作。

这相称于在 AOF rewrite 期间,临时把 appendfsync 设置为了 none,配置如下:

# AOF rewrite 期间,AOF 后台子线程不进行刷盘操作# 相称于在这期间,临时把 appendfsync 设置为了 noneno-appendfsync-on-rewrite yes

当然,开启这个配置项,在 AOF rewrite 期间,如果实例发生宕机,那么此时会丢失更多的数据,性能和数据安全性,你须要权衡后进行选择。

如果占用磁盘资源的是其他运用程序,那就比较大略了,你须要定位到是哪个运用程序在大量写磁盘,然后把这个运用程序迁移到其他机器上实行就好了,避免对 Redis 产生影响。

当然,如果你对 Redis 的性能和数据安全都有很高的哀求,那么建议从硬件层面来优化,改换为 SSD 磁盘,提高磁盘的 IO 能力,担保 AOF 期间有充足的磁盘资源可以利用。
同时尽可能让Redis运行在独立的机器上。

10.Swap优化

1.增加机器的内存,让 Redis 有足够的内存可以利用

2.整理内存空间,开释出足够的内存供 Redis 利用,然后开释 Redis 的 Swap,让 Redis 重新利用内存

开释 Redis 的 Swap 过程常日要重启实例,为了避免重启实例对业务的影响,一样平常会前辈行主从切换,然后开释旧主节点的 Swap,重启旧主节点实例,待从库数据同步完成后,再进行主从切换即可。

预防的办法便是,你须要对 Redis 机器的内存和 Swap 利用情形进行监控,在内存不敷或利用到 Swap 时报警出来,及时处理。

三、Redis变慢了排查步骤

1、获取 Redis 实例在当前环境下的基线性能。

2、是否用了慢查询命令?如果是的话,就利用其他命令替代慢查询命令,或者把聚合打算命令放在客户端做。

3、是否对过期 key 设置了相同的过期韶光?对付批量删除的 key,可以在每个 key 的过期韶光上加一个随机数,避免同时删除。

4、是否存在 bigkey?对付 bigkey 的删除操作,如果你的 Redis 是 4.0 及以上的版本,可以直策应用异步线程机制减少主线程壅塞;如果是 Redis 4.0 以前的版本,可以利用 SCAN 命令迭代删除;对付 bigkey 的凑集查询和聚合操作,可以利用 SCAN 命令在客户端完成。

5、Redis AOF 配置级别是什么?业务层面是否的确须要这一可靠性级别?如果我们须要高性能,同时大概可数据丢失,可以将配置项 no-appendfsync-on-rewrite 设置为 yes,避免 AOF 重写和 fsync 竞争磁盘 IO 资源,导致 Redis 延迟增加。
当然, 如果既须要高性能又须要高可靠性,最好利用高速固态盘作为 AOF 日志的写入盘。

6、Redis 实例的内存利用是否过大?发生 swap 了吗?如果是的话,就增加机器内存,或者是利用 Redis 集群,分摊单机 Redis 的键值对数量和内存压力。
同时,要避免涌现 Redis 和其他内存需求大的运用共享机器的情形。

7、在 Redis 实例的运行环境中,是否启用了透来岁夜页机制?如果是的话,直接关闭内存大页机制就行了。

8、是否运行了 Redis 主从集群?如果是的话,把主库实例的数据量大小掌握在 2~4GB,以免主从复制时,从库因加载大的 RDB 文件而壅塞。

9、是否利用了多核 CPU 或 NUMA 架构的机器运行 Redis 实例?利用多核 CPU 时,可以给 Redis 实例绑定物理核;利用 NUMA 架构时,把稳把 Redis 实例和网络中断处理程序运行在同一个 CPU Socket 上。

标签:

相关文章

森林骨,介绍森林生态系统中的关键元素

森林,作为地球上最大的生态系统之一,拥有着丰富的生物多样性和生态功能。在森林生态系统中,各种生物、非生物因素相互交织,共同维持着生...

科学 2025-01-06 阅读0 评论0