来自- https://mp.weixin.qq.com/s?__biz=MzIzOTU0NTQ0MA==&mid=2247532967&idx=1&sn=19790c981aa33502aa1e3a8abe9cd064&chksm=e92a7ca8de5df5befc6cc534cbabdb847eff41d0b7cef373191e49df38211e74f8064bd5b62f&from=industrynews&version=4.1.3.6112&platform=win#rd
复制代码 、Redis为什么变慢了
1.Redis真的变慢了吗?
对 Redis 进行基准性能测试例如,我的机器配置比较低,当延迟为 2ms 时,我就认为 Redis 变慢了,但是如果你的硬件配置比较高,那么在你的运行环境下,可能延迟是 0.5ms 时就可以认为 Redis 变慢了。所以,你只有了解了你的 Redis 在生产环境服务器上的基准性能,才能进一步评估,当其延迟达到什么程度时,才认为 Redis 确实变慢了。为了避免业务服务器到 Redis 服务器之间的网络延迟,你需要直接在 Redis 服务器上测试实例的响应延迟情况。执行以下命令,就可以测试出这个实例 60 秒内的最大响应延迟:- [/code][code]./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.<br>36481658 total runs (avg latency: 3.2893 microseconds / 3289.32 nanoseconds per run).Worst run took 36x longer than the average latency.
复制代码- [/code]从输出结果可以看到,这 60 秒内的最大响应延迟为 119 微秒(0.119毫秒)。你还可以使用以下命令,查看一段时间内 Redis 的最小、最大、平均访问延迟
- [code]
复制代码- $ 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
复制代码- [/code]如果你观察到的 Redis 运行时延迟是其基线性能的 2 倍及以上,就可以认定 Redis 变慢了。
- 网络对 Redis 性能的影响,一个简单的方法是用 iPerf 这样的工具测试网络极限带宽。
- [code]
复制代码- 服务器端<br># 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客户端<br># 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
复制代码- [/code]
- [b]2.使用复杂度过高的命令[/b]首先,第一步,你需要去查看一下 Redis 的慢日志(slowlog)。Redis 提供了慢日志命令的统计功能,它记录了有哪些命令在执行时耗时比较久。查看 Redis 慢日志之前,你需要设置慢日志的阈值。例如,设置慢日志的阈值为 5 毫秒,并且保留最近 500 条慢日志记录:
- [code]
复制代码- # 命令执行耗时超过 5 毫秒,记录慢日志CONFIG SET slowlog-log-slower-than 5000# 只保留最近 500 条慢日志CONFIG SET slowlog-max-len 500
复制代码- [/code]1.经常使用 O(N) 以上复杂度的命令,例如 SORT、SUNION、ZUNIONSTORE 聚合类命令2.使用 O(N) 复杂度的命令,但 N 的值非常大第一种情况导致变慢的原因在于,Redis 在操作内存数据时,时间复杂度过高,要花费更多的 CPU 资源。第二种情况导致变慢的原因在于,Redis 一次需要返回给客户端的数据过多,更多时间花费在数据协议的组装和网络传输过程中。另外,我们还可以从资源使用率层面来分析,如果你的应用程序操作 Redis 的 OPS 不是很大,但 Redis 实例的 [b]CPU 使用率却很高[/b],那么很有可能是使用了复杂度过高的命令导致的。
-
- [b]3.操作bigkey[/b]
- 如果你查询慢日志发现,并不是复杂度过高的命令导致的,而都是 SET / DEL 这种简单命令出现在慢日志中,那么你就要怀疑你的实例否写入了 bigkey。
- [code]
复制代码- 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)
复制代码- [/code]这里我需要提醒你的是,当执行这个命令时,要注意 2 个问题:
- 1.对线上实例进行 bigkey 扫描时,Redis 的 OPS 会突增,为了降低扫描过程中对 Redis 的影响,最好控制一下扫描的频率,指定 -i 参数即可,它表示扫描过程中每次扫描后休息的时间间隔,单位是秒2.扫描结果中,对于容器类型(List、Hash、Set、ZSet)的 key,只能扫描出元素最多的 key。但一个 key 的元素多,不一定表示占用内存也多,你还需要根据业务情况,进一步评估内存占用情况。
-
- [b]4.集中过期[/b]
- 如果你发现,平时在操作 Redis 时,并没有延迟很大的情况发生,但在某个时间点突然出现一波延时,其现象表现为:[b]变慢的时间点很有规律,例如某个整点,或者每间隔多久就会发生一波延迟。[/b]如果是出现这种情况,那么你需要排查一下,业务代码中是否存在设置大量 key 集中过期的情况。如果有大量的 key 在某个固定时间点集中过期,在这个时间点访问 Redis 时,就有可能导致延时变大。Redis 的过期数据采用被动过期 + 主动过期两种策略:1.被动过期:只有当访问某个 key 时,才判断这个 key 是否已过期,如果已过期,则从实例中删除2.主动过期:Redis 内部维护了一个定时任务,默认每隔 100 毫秒(1秒10次)就会从全局的过期哈希表中随机取出 20 个 key,然后删除其中过期的 key,如果过期 key 的比例超过了 25%,则继续重复此过程,直到过期 key 的比例下降到 25% 以下,或者这次任务的执行耗时超过了 25 毫秒,才会退出循环注意,[b]这个主动过期 key 的定时任务,是在 Redis 主线程中执行的。[/b]也就是说如果在执行主动过期的过程中,出现了需要大量删除过期 key 的情况,那么此时应用程序在访问 Redis 时,必须要等待这个过期任务执行结束,Redis 才可以服务这个客户端请求。如果此时需要过期删除的是一个 bigkey,那么这个耗时会更久。而且,[b]这个操作延迟的命令并不会记录在慢日志中。[/b]因为慢日志中[b]只记录一个命令真正操作内存数据的耗时[/b],而 Redis 主动删除过期 key 的逻辑,是在命令真正执行之前执行的。
-
- [b]5.实例内存达到上限[/b]
- 当我们把 Redis 当做纯缓存使用时,通常会给这个实例设置一个内存上限 maxmemory,然后设置一个数据淘汰策略。当 Redis 内存达到 maxmemory 后,每次写入新的数据之前,[b]Redis 必须先从实例中踢出一部分数据,让整个实例的内存维持在 maxmemory 之下[/b],然后才能把新数据写进来。这个踢出旧数据的逻辑也是需要消耗时间的,而具体耗时的长短,要取决于你配置的淘汰策略:
- [list]
- [*]allkeys-lru:不管 key 是否设置了过期,淘汰最近最少访问的 key
- [*][b]volatile-lru:只淘汰最近最少访问、并设置了过期时间的 key[/b]
- [*]allkeys-random:不管 key 是否设置了过期,随机淘汰 key
- [*]volatile-random:只随机淘汰设置了过期时间的 key
- [*]allkeys-ttl:不管 key 是否设置了过期,淘汰即将过期的 key
- [*]noeviction:不淘汰任何 key,实例内存达到 maxmeory 后,再写入新数据直接返回错误
- [*]allkeys-lfu:不管 key 是否设置了过期,淘汰访问频率最低的 key(4.0+版本支持)
- [*]volatile-lfu:只淘汰访问频率最低、并设置了过期时间 key(4.0+版本支持)
- [/list][b]一般最常使用的是 allkeys-lru / volatile-lru 淘汰策略,它们的处理逻辑是,每次从实例中随机取出一批 key(这个数量可配置),然后淘汰一个最少访问的 key,之后把剩下的 key 暂存到一个池子中,继续随机取一批 key,并与之前池子中的 key 比较,再淘汰一个最少访问的 key。以此往复,直到实例内存降到 maxmemory 之下。[/b]
- 需要注意的是,Redis 的淘汰数据的逻辑与删除过期 key 的一样,也是在命令真正执行之前执行的,也就是说它也会增加我们操作 Redis 的延迟,而且,写 OPS 越高,延迟也会越明显。
- [img]https://img2023.cnblogs.com/blog/333678/202304/333678-20230428101636573-1471472459.png[/img]
-
- 如果此时你的 Redis 实例中还存储了 bigkey,那么[b]在淘汰删除 bigkey 释放内存时,也会耗时比较久。[/b]
-
- [b]6.fork耗时严重[/b]
- 当 Redis 开启了后台 RDB 和 AOF rewrite 后,在执行时,它们都需要主进程创建出一个子进程进行数据的持久化。
- 主进程创建子进程,会调用操作系统提供的 fork 函数。
- 而 fork 在执行过程中,[b]主进程需要拷贝自己的内存页表给子进程[/b],如果这个实例很大,那么这个拷贝的过程也会比较耗时。
- 而且这个 fork 过程会消耗大量的 CPU 资源,在完成 fork 之前,整个 Redis 实例会被阻塞住,无法处理任何客户端请求。
- 如果此时你的 CPU 资源本来就很紧张,那么 fork 的耗时会更长,甚至达到秒级,这会严重影响 Redis 的性能。
- 那如何确认确实是因为 fork 耗时导致的 Redis 延迟变大呢?
- 你可以在 Redis 上执行 INFO 命令,查看 latest_fork_usec 项,单位微秒。
- [code]
复制代码- # 上一次 fork 耗时,单位微秒latest_fork_usec:59477
复制代码- [/code]这个时间就是主进程在 fork 子进程期间,整个实例阻塞无法处理客户端请求的时间。
- 如果你发现这个耗时很久,就要警惕起来了,这意味在这期间,你的整个 Redis 实例都处于不可用的状态。
- 除了数据持久化会生成 RDB 之外,当主从节点第一次建立数据同步时,主节点也创建子进程生成 RDB,然后发给从节点进行一次全量同步,所以,这个过程也会对 Redis 产生性能影响。
- [img]https://img2023.cnblogs.com/blog/333678/202304/333678-20230428101624954-1219471129.png[/img]
-
-
- [b]7.开启内存大页[/b]
- 除了上面讲到的子进程 RDB 和 AOF rewrite 期间,fork 耗时导致的延时变大之外,这里还有一个方面也会导致性能问题,这就是操作系统是否开启了[b]内存大页机制[/b]。
- 什么是内存大页?
- 我们都知道,应用程序向操作系统申请内存时,是按[b]内存页[/b]进行申请的,而常规的内存页大小是 4KB。
- Linux 内核从 2.6.38 开始,支持了[b]内存大页机制[/b],该机制允许应用程序以 2MB 大小为单位,向操作系统申请内存。
- 应用程序每次向操作系统申请的内存单位变大了,但这也意味着申请内存的耗时变长。
- [b]这对 Redis 会有什么影响呢?[/b]
- 当 Redis 在执行后台 RDB,采用 fork 子进程的方式来处理。但主进程 fork 子进程后,此时的[b]主进程依旧是可以接收写请求的[/b],而进来的写请求,会采用 Copy On Write(写时复制)的方式操作内存数据。
- 也就是说,主进程一旦有数据需要修改,Redis 并不会直接修改现有内存中的数据,而是[b]先将这块内存数据拷贝出来,再修改这块新内存的数据[/b],这就是所谓的「写时复制」。
- 写时复制你也可以理解成,谁需要发生写操作,谁就需要先拷贝,再修改。
- 这样做的好处是,父进程有任何写操作,并不会影响子进程的数据持久化(子进程只持久化 fork 这一瞬间整个实例中的所有数据即可,不关心新的数据变更,因为子进程只需要一份内存快照,然后持久化到磁盘上)。
- 但是请注意,主进程在拷贝内存数据时,这个阶段就涉及到新内存的申请,如果此时操作系统开启了内存大页,那么在此期间,客户端即便只修改 10B 的数据,[b]Redis 在申请内存时也会以 2MB 为单位向操作系统申请,申请内存的耗时变长,进而导致每个写请求的延迟增加,影响到 Redis 性能。[/b]
- 同样地,如果这个写请求操作的是一个 bigkey,那主进程在拷贝这个 bigkey 内存块时,一次申请的内存会更大,时间也会更久。可见,bigkey 在这里又一次影响到了性能。
-
- [b]8.开启AOF[/b]
- 前面我们分析了 RDB 和 AOF rewrite 对 Redis 性能的影响,主要关注点在 fork 上。
- 其实,关于数据持久化方面,还有影响 Redis 性能的因素,这次我们重点来看 AOF 数据持久化。
- 如果你的 AOF 配置不合理,还是有可能会导致性能问题。
- 当 Redis 开启 AOF 后,其工作原理如下:
- [b]1.Redis 执行写命令后,把这个命令写入到 AOF 文件内存中(write 系统调用)[/b]
- [b]2.Redis 根据配置的 AOF 刷盘策略,把 AOF 内存数据刷到磁盘上(fsync 系统调用)[/b]
- 为了保证 AOF 文件数据的安全性,Redis 提供了 3 种刷盘机制:
- 1.appendfsync always:主线程每次执行写操作后立即刷盘,此方案会占用比较大的磁盘 IO 资源,但数据安全性最高
- 2.appendfsync no:主线程每次写操作只写内存就返回,内存数据什么时候刷到磁盘,交由操作系统决定,此方案对性能影响最小,但数据安全性也最低,Redis 宕机时丢失的数据取决于操作系统刷盘时机
- 3.appendfsync everysec:主线程每次写操作只写内存就返回,然后由后台线程每隔 1 秒执行一次刷盘操作(触发fsync系统调用),此方案对性能影响相对较小,但当 Redis 宕机时会丢失 1 秒的数据
- 看到这里,我猜你肯定和大多数人的想法一样,选比较折中的方案 appendfsync everysec 就没问题了吧?
- 这个方案优势在于,Redis 主线程写完内存后就返回,具体的刷盘操作是放到后台线程中执行的,后台线程每隔 1 秒把内存中的数据刷到磁盘中。
- 这种方案既兼顾了性能,又尽可能地保证了数据安全,是不是觉得很完美?
- [b]但是,这里我要给你泼一盆冷水了,采用这种方案你也要警惕一下,因为这种方案还是存在导致 Redis 延迟变大的情况发生,甚至会阻塞整个 Redis。[/b]
- 你试想这样一种情况:当 Redis 后台线程在执行 AOF 文件刷盘时,如果此时磁盘的 IO 负载很高,那这个后台线程在执行刷盘操作(fsync系统调用)时就会被阻塞住。
- 此时的主线程依旧会接收写请求,紧接着,主线程又需要把数据写到文件内存中(write 系统调用),当主线程使用后台子线程执行了一次 fsync,需要再次把新接收的操作记录写回磁盘时,如果主线程发现上一次的 fsync 还没有执行完,那么它就会阻塞。所以,如果后台子线程执行的 fsync 频繁阻塞的话(比如 AOF 重写占用了大量的磁盘 IO 带宽),主线程也会阻塞,导致 Redis 性能变慢。
- [img]https://img2023.cnblogs.com/blog/333678/202304/333678-20230428101601048-842260692.png[/img]
-
- 看到了么?在这个过程中,主线程依旧有阻塞的风险。
- 所以,尽管你的 AOF 配置为 appendfsync everysec,也不能掉以轻心,要警惕磁盘压力过大导致的 Redis 有性能问题。
- 那什么情况下会导致磁盘 IO 负载过大?以及如何解决这个问题呢?
- 我总结了以下几种情况,你可以参考进行问题排查:
- [b]1.子进程正在执行 AOF rewrite,这个过程会占用大量的磁盘 IO 资源[/b]
- [b]2.有其他应用程序在执行大量的写文件操作,也会占用磁盘 IO 资源[/b]
- [b]对于情况1,说白了就是,Redis 的 AOF 后台子线程刷盘操作,撞上了子进程 AOF rewrite![/b]
-
- [b]9.绑定CPU[/b]
- 很多时候,我们在部署服务时,为了提高服务性能,降低应用程序在多个 CPU 核心之间的上下文切换带来的性能损耗,通常采用的方案是进程绑定 CPU 的方式提高性能。
- 我们都知道,一般现代的服务器会有多个 CPU,而每个 CPU 又包含多个物理核心,每个物理核心又分为多个逻辑核心,每个物理核下的逻辑核共用 L1/L2 Cache。
- 而 Redis Server 除了主线程服务客户端请求之外,还会创建子进程、子线程。
- 其中子进程用于数据持久化,而子线程用于执行一些比较耗时操作,例如异步释放 fd、异步 AOF 刷盘、异步 lazy-free 等等。
- 如果你把 Redis 进程只绑定了一个 CPU 逻辑核心上,那么当 Redis 在进行数据持久化时,fork 出的子进程会继承父进程的 CPU 使用偏好。
- [b]而此时的子进程会消耗大量的 CPU 资源进行数据持久化(把实例数据全部扫描出来需要耗费CPU),这就会导致子进程会与主进程发生 CPU 争抢,进而影响到主进程服务客户端请求,访问延迟变大。[/b]
- 这就是 Redis 绑定 CPU 带来的性能问题。
-
- [b]10.使用Swap[/b]
- 如果你发现 Redis 突然变得非常慢,[b]每次的操作耗时都达到了几百毫秒甚至秒级[/b],那此时你就需要检查 Redis 是否使用到了 Swap,在这种情况下 Redis 基本上已经无法提供高性能的服务了。
- 什么是 Swap?为什么使用 Swap 会导致 Redis 的性能下降?
- 如果你对操作系统有些了解,就会知道操作系统为了缓解内存不足对应用程序的影响,允许把一部分内存中的数据换到磁盘上,以达到应用程序对内存使用的缓冲,这些内存数据被换到磁盘上的区域,就是 Swap。
- 问题就在于,当内存中的数据被换到磁盘上后,Redis 再访问这些数据时,就需要从磁盘上读取,访问磁盘的速度要比访问内存慢几百倍!
- [b]尤其是针对 Redis 这种对性能要求极高、性能极其敏感的数据库来说,这个操作延时是无法接受的。[/b]
- 此时,你需要检查 Redis 机器的内存使用情况,确认是否存在使用了 Swap。
- 你可以通过以下方式来查看 Redis 进程是否使用到了 Swap:
- [code]
复制代码- # 先找到 Redis 的进程 ID$ ps -aux | grep redis-server# 查看 Redis Swap 使用情况$ cat /proc/$pid/smaps | egrep '^(Swap|Size)'
复制代码 输出结果如下:- [/code][code]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
复制代码- [/code]每一行 Size 表示 Redis 所用的一块内存大小,Size 下面的 Swap 就表示这块 Size 大小的内存,有多少数据已经被换到磁盘上了,如果这两个值相等,说明这块内存的数据都已经完全被换到磁盘上了。
- 如果只是少量数据被换到磁盘上,例如每一块 Swap 占对应 Size 的比例很小,那影响并不是很大。[b]如果是几百兆甚至上 GB 的内存被换到了磁盘上[/b],那么你就需要警惕了,这种情况 Redis 的性能肯定会急剧下降。
-
- [b]11.碎片整理[/b]
- Redis 的数据都存储在内存中,当我们的应用程序频繁修改 Redis 中的数据时,就有可能会导致 Redis 产生内存碎片。
- 内存碎片会降低 Redis 的内存使用率,我们可以通过执行 INFO 命令,得到这个实例的内存碎片率:
- [code]
复制代码- # Memoryused_memory:5709194824used_memory_human:5.32Gused_memory_rss:8264855552used_memory_rss_human:7.70G...mem_fragmentation_ratio:1.45
复制代码- [/code]这个内存碎片率是怎么计算的?
- 很简单,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 版本,它正好提供了自动碎片整理的功能,可以通过配置开启碎片自动整理。
- [b]但是,开启内存碎片整理,它也有可能会导致 Redis 性能下降。[/b]
- 原因在于,Redis 的碎片整理工作是也在[b]主线程[/b]中执行的,当其进行碎片整理时,必然会消耗 CPU 资源,产生更多的耗时,从而影响到客户端的请求。
- 所以,当你需要开启这个功能时,最好提前测试评估它对 Redis 的影响。
- Redis 碎片整理的参数配置如下:
- [code]# 开启自动内存碎片整理(总开关)activedefrag yes<br># 内存使用 100MB 以下,不进行碎片整理active-defrag-ignore-bytes 100mb<br># 内存碎片率超过 10%,开始碎片整理active-defrag-threshold-lower 10# 内存碎片率超过 100%,尽最大努力碎片整理active-defrag-threshold-upper 100<br># 内存碎片整理占用 CPU 资源最小百分比active-defrag-cycle-min 1# 内存碎片整理占用 CPU 资源最大百分比active-defrag-cycle-max 25<br># 碎片整理期间,对于 List/Set/Hash/ZSet 类型元素一次 Scan 的数量active-defrag-max-scan-fields 1000
复制代码 二、Redis如何优化
1.慢查询优化
1.尽量不使用 O(N) 以上复杂度过高的命令,对于数据的聚合操作,放在客户端做
2.执行 O(N) 命令,保证 N 尽量的小(推荐 N 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) ""[/code]- [/code]注意:DEL命令,还是并发阻塞的删除操作
- [size=2][b]FLUSHALL/FLUSHDB ASYNC[/b][/size]
- [code]
复制代码 8.2 被动删除键使用lazy free
lazy free应用于被动删除中,目前有4种场景,每种场景对应一个配置参数;默认都是关闭。- [/code][size=2][b]lazyfree-lazy-eviction[/b][/size]
- 针对redis内存使用达到maxmeory,并设置有淘汰策略时;在被动淘汰键时,是否采用lazy free机制;因为此场景开启lazy free, 可能使用淘汰键的内存释放不及时,导致redis内存超用,超过maxmemory的限制。此场景使用时,请结合业务测试。([b]生产环境不建议设置yes[/b])
- [b]lazyfree-lazy-expire[/b]
- 针对设置有TTL的键,达到过期后,被redis清理删除时是否采用lazy free机制;此场景建议开启,因TTL本身是自适应调整的速度。
- [size=2][b]lazyfree-lazy-server-del[/b][/size]
- 针对有些指令在处理已存在的键时,会带有一个隐式的DEL键的操作。如rename命令,当目标键已存在,redis会先删除目标键,如果这些目标键是一个big key,那就会引入阻塞删除的性能问题。此参数设置就是解决这类问题,建议可开启。
- [size=2][b]slave-lazy-flush[/b][/size]
- 针对slave进行全量数据同步,slave在加载master的RDB文件前,会运行flushall来清理自己的数据场景, 参数设置决定是否采用异常flush机制。如果内存变动不大,建议可开启。可减少全量同步耗时,从而减少主库因输出缓冲区爆涨引起的内存使用增长。
- [size=3]8.3 lazy free的监控[/size]
- lazy free能监控的数据指标,只有一个值:lazyfree_pending_objects,表示redis执行lazy free操作,在等待被实际回收内容的键个数。并不能体现单个大键的元素个数或等待lazy free回收的内存大小。所以此值有一定参考值,可监测redis lazy free的效率或堆积键数量;比如在flushall async场景下会有少量的堆积。
- [code]
复制代码- taskset -c 0 ./redis-server
复制代码- [/code][b]注意事项:[/b]unlink命令入口函数unlinkCommand()和del调用相同函数delGenericCommand()进行删除KEY操作,使用lazy标识是否为lazyfree调用。如果是lazyfree,则调用dbAsyncDelete()函数。[b]但并非每次unlink命令就一定启用lazy free,redis会先判断释放KEY的代价(cost),当cost大于LAZYFREE_THRESHOLD(64)才进行lazy free.[/b]释放key代价计算函数lazyfreeGetFreeEffort(),集合类型键,且满足对应编码,cost就是集合键的元数个数,否则cost就是1. 举例:1 一个包含100元素的list key, 它的free cost就是100 2 一个512MB的string key, 它的free cost是1 所以可以看出,redis的lazy free的cost计算主要时间复杂度相关。
- [b]9.AOF优化[/b]
- Redis 提供了一个配置项,当子进程在 AOF rewrite 期间,可以让后台子线程不执行刷盘(不触发 fsync 系统调用)操作。
- 这相当于在 AOF rewrite 期间,临时把 appendfsync 设置为了 none,配置如下:
- [code]
复制代码- # 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
复制代码 [code][/code]当然,开启这个配置项,在 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 上。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |