18 - 19 | 波动的响应延迟:如何应对变慢的Redis?

2024-04-20 21:32

本文主要是介绍18 - 19 | 波动的响应延迟:如何应对变慢的Redis?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • Redis核心技术与实战
    • 实践篇
      • 18 - 19 | 波动的响应延迟:如何应对变慢的Redis?
        • Redis 真的变慢了吗?
          • 方法一:查看 Redis 的响应延迟。
          • 方法二:基于当前环境下的 Redis 基线性能做判断
        • 如何应对 Redis 变慢?
          • Redis 自身操作特性的影响
        • 文件系统:AOF 模式
        • 操作系统:swap
        • 操作系统:内存大页
      • 小结


Redis核心技术与实战

实践篇

18 - 19 | 波动的响应延迟:如何应对变慢的Redis?

Redis 真的变慢了吗?
方法一:查看 Redis 的响应延迟。

Redis 命令的执行时间突然就增长到了几秒,基本就可以认定 Redis 变慢了。

这种方法是看 Redis 延迟的绝对值,但是,在不同的软硬件环境下,Redis 本身的绝对性能并不相同。比如,在环境 A 中,当延迟为 1ms 时,判定 Redis 变慢了,但是环境 B 硬件配置高,那么,在环境 B 下,可能延迟是 0.2ms 的时候,就可以认定 Redis 变慢了。

方法二:基于当前环境下的 Redis 基线性能做判断

所谓的基线性能,就是指一个系统在低压力、无干扰下的基本性能,这个性能只由当前的软硬件配置决定。

从 2.8.7 版本开始,redis-cli 命令提供了 –intrinsic-latency 选项,可以用来监测和统计测试期间内的最大延迟,这个延迟可以作为 Redis 的基线性能。

./redis-cli --intrinsic-latency 120
Max 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.

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

在虚拟化环境(例如虚拟机或容器)中,由于增加了虚拟化软件层,与物理机相比,虚拟机或容器本身就会引入一定的性能开销,所以基线性能会高一些。

如何应对 Redis 变慢?

Redis 自身的操作特性、文件系统和操作系统,它们是影响 Redis 性能的三大要素。
在这里插入图片描述

Redis 自身操作特性的影响

1. 慢查询命令

当 Redis 性能变慢时,可以通过 Redis 日志(slowlog),或者是 latency monitor 工具,查询变慢的请求,根据请求对应的具体命令以及官方文档,确认下是否采用了复杂度高的慢查询命令。

如果的确有大量的慢查询命令,有两种处理方式:

  • 用其他高效命令代替。比如说,如果需要返回一个 SET 中的所有成员时,不要使用 SMEMBERS 命令,而是要使用 SSCAN 多次迭代返回,避免一次返回大量数据,造成线程阻塞。
  • 当需要执行排序、交集、并集操作时,可以在客户端完成,而不要用 SORT、SUNION、SINTER 这些命令,以免拖慢 Redis 实例。

易忽略的慢查询命令 KEYS。它用于返回和输入模式匹配的所有 key,例如,以下命令返回所有包含“name”字符串的 keys。

redis> KEYS *name*
1) "lastname"
2) "firstname"

因为 KEYS 命令需要遍历存储的键值对,所以操作延时高。所以,KEYS 命令一般不被建议用于生产环境中。

2. 过期 key 操作

默认情况下,Redis 每 100 毫秒(即每秒十次)会删除一些过期 key,具体的算法如下:

  1. 采样 ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 个数的 key,并将其中过期的 key 全部删除;
  2. 如果超过 25% 的 key 过期了,则重复删除的过程,直到过期 key 的比例降至 25% 以下。

ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 是 Redis 的一个参数,默认是 20,那么,一秒内基本有 200 个过期 key 会被删除(200 = 1000 / 100 * 20,每秒执行10次,每次20个,共200)。

如果触发了上面这个算法的第二条,Redis 就会一直删除以释放内存空间。注意,删除操作是阻塞的(Redis 4.0 后可以用异步线程机制来减少阻塞影响)。所以,一旦该条件触发,Redis 的线程就会一直执行删除,这样一来,就没办法正常服务其他的键值操作,就会进一步引起其他键值操作的延迟增加,Redis 就会变慢。

算法的第二条是怎么被触发的?

频繁使用带有相同时间参数的 EXPIREAT(作用和 EXPIRE 类似,都用于为 key 设置生存时间,
不同的地方在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳) 命令设置过期 key,这就会导致,在同一秒内有大量的 key 同时过期。

检查业务代码在使用 EXPIREAT 命令设置 key 过期时间时,是否使用了相同的 UNIX 时间戳,有没有使用 EXPIRE 命令给批量的 key 设置相同的过期秒数。 因为,这都会造成大量 key 在同一时间过期,导致性能变慢。

如果一批 key 的确是同时过期,可以在 EXPIREAT 和 EXPIRE 的过期时间参数上,加上一个一定大小范围内的随机数,这样,既保证了 key 在一个邻近时间范围内被删除,又避免了同时过期造成的压力。

文件系统:AOF 模式

为了保证数据可靠性,Redis 会采用 AOF 日志或 RDB 快照。其中,AOF 日志提供了三种日志写回策略:no、everysec、always。这三种写回策略依赖文件系统的两个系统调用完成,也就是 write 和 fsync。

write 只要把日志记录写到内存缓冲区,就可以返回,并不需要等待日志实际写回到磁盘;而 fsync 需要把日志记录写回到磁盘后才能返回,时间较长。

在这里插入图片描述

当写回策略配置为 everysec 和 always 时,Redis 需要调用 fsync 把日志写回磁盘。

在使用 everysec 时,Redis 允许丢失一秒的操作记录,所以,Redis 主线程并不需要确保每个操作记录日志都写回磁盘。而且,fsync 的执行时间很长,如果是在 Redis 主线程中执行 fsync,就容易阻塞主线程。

当写回策略配置为 everysec 时,Redis 会使用后台的子线程异步完成 fsync 的操作。

对于 always 策略来说,Redis 需要确保每个操作记录日志都写回磁盘,如果用后台子线程异步完成,主线程就无法及时地知道每个操作是否已经完成,不符合 always 策略的要求。

always 策略并不使用后台子线程来执行。

在使用 AOF 日志时,为了避免日志文件不断增大,Redis 会执行 AOF 重写,生成体量缩小的新的 AOF 日志文件。AOF 重写本身需要的时间很长,也容易阻塞 Redis 主线程,所以,Redis 使用子进程来进行 AOF 重写。

潜在的风险点:AOF 重写会对磁盘进行大量 IO 操作,同时,fsync 又需要等到数据写到磁盘后才能返回,所以,当 AOF 重写的压力比较大时,就会导致 fsync 被阻塞。虽然 fsync 是由后台子线程负责执行的,但是,主线程会监控 fsync 的执行进度。

当主线程使用后台子线程执行了一次 fsync,需要再次把新接收的操作记录写回磁盘时,如果主线程发现上一次的 fsync 还没有执行完,那么它就会阻塞。

如果后台子线程执行的 fsync 频繁阻塞(比如 AOF 重写占用了大量的磁盘 IO 带宽),主线程也会阻塞,导致 Redis 性能变慢。

在这里插入图片描述

排查和解决建议

如果业务应用对延迟非常敏感,但同时允许一定量的数据丢失,那么,可以把配置项 no-appendfsync-on-rewrite 设置为 yes,如下所示:

no-appendfsync-on-rewrite yes

no-appendfsync-on-rewrite 设置为 yes 时,表示在 AOF 重写时,不进行 fsync 操作。 也就是说,Redis 实例把写命令写到内存后,不调用后台线程进行 fsync 操作,就可以直接返回。当然,如果此时实例发生宕机,就会导致数据丢失。反之,如果 no-appendfsync-on-rewrite 设置为 no(默认配置),在 AOF 重写时,Redis 实例仍然会调用后台线程进行 fsync 操作。

如果的确需要高性能,同时也需要高可靠数据保证,建议采用高速的固态硬盘作为 AOF 日志的写入设备。

操作系统:swap

内存 swap 是操作系统里将内存数据在内存和磁盘间来回换入和换出的机制,涉及到磁盘的读写。 所以,一旦触发 swap,无论是被换入数据的进程,还是被换出数据的进程,其性能都会受到慢速磁盘读写的影响。

Redis 是内存数据库,内存使用量大,如果没有控制好内存的使用量,或者和其他内存需求大的应用一起运行,就可能受到 swap 的影响,而导致性能变慢。

正常情况下,Redis 的操作是直接通过访问内存就能完成,一旦 swap 被触发,Redis 的请求操作需要等到磁盘数据读写完成才行。 swap 触发后影响的是 Redis 主 IO 线程,这会极大地增加 Redis 的响应时间。

通常,触发 swap 的原因主要是物理机器内存不足,对于 Redis 而言,有两种常见的情况:

  1. Redis 实例自身使用了大量的内存,导致物理机器的可用内存不足;
  2. 和 Redis 实例在同一台机器上运行的其他进程,在进行大量的文件读写操作。文件读写本身会占用系统内存,这会导致分配给 Redis 实例的内存量变少,进而触发 Redis 发生 swap。

解决思路:增加机器的内存或者使用 Redis 集群(分摊每个实例服务的数据量,进而减少每个实例所需的内存量)。

查看 swap 使用情况

  1. 操作系统本身会在后台记录每个进程的 swap 使用情况,即有多少数据量发生了 swap。可以先通过下面的命令查看 Redis 的进程号:
$ redis-cli info | grep process_id
process_id: 5332
  1. 然后,进入 Redis 所在机器的 /proc 目录下的该进程目录中:
$ cd /proc/5332
  1. 最后,运行 cat smaps | egrep ‘^(Swap|Size)’ 命令,查看该 Redis 进程的使用情况:
$cat smaps | egrep '^(Swap|Size)'
Size: 584 kB
Swap: 0 kB
Size: 4 kB
Swap: 4 kB
Size: 4 kB
Swap: 0 kB
Size: 462044 kB
Swap: 462008 kB
Size: 21392 kB
Swap: 0 kB

每一行 Size 表示的是 Redis 实例所用的一块内存大小,而 Size 下方的 Swap 与之对应,表示这块 Size 大小的内存区域有多少已经被换出到磁盘上。如果这两个值相等,就表示这块内存区域已经完全被换出到磁盘。

注意:当出现百 MB,甚至 GB 级别的 swap 大小时,就表明,此时,Redis 实例的内存压力很大,很有可能会变慢。所以,swap 的大小是排查 Redis 性能变慢是否由 swap 引起的重要指标。

操作系统:内存大页

Linux 内核从 2.6.38 开始支持内存大页机制,该机制支持 2MB 大小的内存页分配,而常规的内存页分配是按 4KB 的粒度来执行的。

虽然内存大页可以给 Redis 带来内存分配方面的收益,但是,当Redis 在持久化数据时,客户端想要修改正在进行持久化的数据,Redis 就会采用写时复制机制。如果采用内存大页,那么,即使客户端请求只修改 100B 的数据,Redis 也需要拷贝 2MB 的大页。相反,如果是常规内存页机制,只用拷贝 4KB。

当客户端请求修改或新写入数据较多时,内存大页机制将导致大量的拷贝,这就会影响 Redis 正常的访存操作,最终导致性能变慢。

解决方案:关闭内存大页。

cat /sys/kernel/mm/transparent_hugepage/enabled

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

在实际生产环境中部署时,建议不要使用内存大页机制,只需要执行下面的命令即可:

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

小结

Redis(4.0 以后版本) 变慢的原因及应对:

(1)慢查询命令
用其他高效命令代替或聚合操作在客户端完成。

(2)AOF 重写阻塞 fsync
no-appendfsync-on-rewrite 设置为 yes 。

(3)swap 被触发
增加机器的内存或者使用 Redis 集群。

(4)Linux 内存大页机制被启用
关闭内存大页机制。

这篇关于18 - 19 | 波动的响应延迟:如何应对变慢的Redis?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/921370

相关文章

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

ActiveMQ—消息特性(延迟和定时消息投递)

ActiveMQ消息特性:延迟和定时消息投递(Delay and Schedule Message Delivery) 转自:http://blog.csdn.net/kimmking/article/details/8443872 有时候我们不希望消息马上被broker投递出去,而是想要消息60秒以后发给消费者,或者我们想让消息没隔一定时间投递一次,一共投递指定的次数。。。 类似

Redis中使用布隆过滤器解决缓存穿透问题

一、缓存穿透(失效)问题 缓存穿透是指查询一个一定不存在的数据,由于缓存中没有命中,会去数据库中查询,而数据库中也没有该数据,并且每次查询都不会命中缓存,从而每次请求都直接打到了数据库上,这会给数据库带来巨大压力。 二、布隆过滤器原理 布隆过滤器(Bloom Filter)是一种空间效率很高的随机数据结构,它利用多个不同的哈希函数将一个元素映射到一个位数组中的多个位置,并将这些位置的值置

简单的角色响应鼠标而移动

actor类 //处理移动距离,核心是找到角色坐标在世界坐标的向量的投影(x,y,z),然后在世界坐标中合成,此CC是在地面行走,所以Y轴投影始终置为0; using UnityEngine; using System.Collections; public class actor : MonoBehaviour { public float speed=0.1f; CharacterCo

Lua 脚本在 Redis 中执行时的原子性以及与redis的事务的区别

在 Redis 中,Lua 脚本具有原子性是因为 Redis 保证在执行脚本时,脚本中的所有操作都会被当作一个不可分割的整体。具体来说,Redis 使用单线程的执行模型来处理命令,因此当 Lua 脚本在 Redis 中执行时,不会有其他命令打断脚本的执行过程。脚本中的所有操作都将连续执行,直到脚本执行完成后,Redis 才会继续处理其他客户端的请求。 Lua 脚本在 Redis 中原子性的原因

laravel框架实现redis分布式集群原理

在app/config/database.php中配置如下: 'redis' => array('cluster' => true,'default' => array('host' => '172.21.107.247','port' => 6379,),'redis1' => array('host' => '172.21.107.248','port' => 6379,),) 其中cl

Redis的rehash机制

在Redis中,键值对(Key-Value Pair)存储方式是由字典(Dict)保存的,而字典底层是通过哈希表来实现的。通过哈希表中的节点保存字典中的键值对。我们知道当HashMap中由于Hash冲突(负载因子)超过某个阈值时,出于链表性能的考虑,会进行Resize的操作。Redis也一样。 在redis的具体实现中,使用了一种叫做渐进式哈希(rehashing)的机制来提高字典的缩放效率,避

MySQL主从同步延迟原理及解决方案

概述 MySQL的主从同步是一个很成熟的架构,优点为: ①在从服务器可以执行查询工作(即我们常说的读功能),降低主服务器压力; ②在从主服务器进行备份,避免备份期间影响主服务器服务; ③当主服务器出现问题时,可以切换到从服务器。 相信大家对于这些好处已经非常了解了,在项目的部署中也采用这种方案。但是MySQL的主从同步一直有从库延迟的问题,那么为什么会有这种问题。这种问题如何解决呢? MyS

【吊打面试官系列-Redis面试题】说说 Redis 哈希槽的概念?

大家好,我是锋哥。今天分享关于 【说说 Redis 哈希槽的概念?】面试题,希望对大家有帮助; 说说 Redis 哈希槽的概念? Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽, 集群的每个节点负责一部分 hash 槽。