当前位置: 首页 > 科技观察

当Redis有高延迟时,会发生什么?

时间:2023-03-21 11:34:11 科技观察

Redis是一种内存数据库,将数据存储在内存中,读写效率远高于传统的将数据存储在磁盘上的数据库。但是,当Redis也出现延迟的时候,我们需要深入了解原因,才能快速排查问题,解决Redis的延迟问题。一个命令执行过程在本文的场景中,延迟是指从客户端延迟客户端发送命令到客户端收到命令返回值之间的时间间隔。那么我们先来看看一个Redis命令执行的步骤,每一步出现问题都可能导致高延迟。上图是Redis客户端发送一条命令的执行过程示意图。绿色的是执行步骤,蓝色的是高延迟的可能原因。网络连接限制、网络传输速率和CPU性能是所有服务器上都可能出现的性能问题。但Redis有其独特的问题,可能会导致高延迟:命令或数据结构滥用、持久阻塞和内存交换。更致命的是Redis采用单线程和事件驱动的机制来处理网络请求。有相应的连接响应处理器、命令请求处理器和命令回复处理器来处理客户端网络请求事件。事件继续进行到队列中的下一个事件。处理一个命令的高延迟会影响下一个排队的其他命令。针对高延迟,Redis原生提供了慢查询统计功能。执行slowlogget{n}命令获取最新的n条慢查询命令。默认情况下,所有执行时间超过10毫秒(可配置)的命令都会被记录在一个固定长度的队列中。在线示例,建议设置为1毫秒,方便及时检测毫秒级以上的命令。#超过slowlog-log-slower-than阈值的命令会被记录到慢查询队列#队列的最大长度为slowlog-max-lenslowlog-log-slower-than10000slowlog-max-len128如果命令执行时间以毫秒为单位,实例实际OPS只有1000左右,慢查询队列默认长度为128,可以适当调整。慢查询本身只记录命令执行时间,不包括数据网络传输时间和命令排队时间。因此,客户端发生阻塞异常后,可能不是当前命令慢,而是在等待其他命令执行。需要重点对比异常和慢查询发生的时间点,确认是否有慢查询导致的命令阻塞队列。slowlog的输出格式如下。第一个字段表示记录在所有慢日志中的序号,最新记录显示在最前面;第二个字段是记录这条记录时的系统时间,可以使用date命令转换为友好格式的第三个字段表示这条命令的响应时间,以us为单位(微秒);第四个字段是对应的Redis操作。>slowlogget1)1)(integer)262)(integer)14502531333)(integer)430974)1)"flushdb"我们来看看命令或数据结构的不合理使用、持久阻塞和内存交换。高延迟问题。不合理的命令或数据结构一般来说,Redis执行命令的速度非常快,但是当数据量达到一定程度时,有些命令的执行时间会非常长,比如执行一个包含几万个元素的哈希结构。对于hgetall操作,由于数据量大,命令算法复杂度为O(n),所以这条命令的执行速度肯定很慢。这个问题是命令和数据结构使用不合理的典型问题。对于高并发场景,我们应该尽量避免在大对象上执行算法复杂度超过O(n)的命令。对于key值较多的hash结构,可以使用scan系列命令一步步遍历,而不是直接使用hgetall全部获取。Redis本身提供了发现大对象的工具,对应命令:redis-cli-h{ip}-p{port}bigkeys。该命令会使用scan从指定的RedisDB中不断采样,实时输出当时获取的值占用空间最大的key值,最后给出各种数据结构最大key的汇总报告。持久化阻塞对于开启了持久化功能的Redis节点,需要检查是否是持久化导致的阻塞。持久化导致主线程阻塞的操作主要有:fork阻塞,AOF刷盘阻塞。当RDB和AOF改写时发生fork操作,Redis主线程调用fork操作生成共享内存的子进程,子进程完成相应的持久化工作。如果fork操作本身耗时过长,势必导致主线程阻塞。Redis执行fork操作产生的子进程内存占用与父进程相同,理论上需要两倍的物理内存才能完成相应的操作。但是,Linux具有写时复制技术。父进程和子进程将共享同一个物理内存页。父进程在处理写请求时,会复制一份需要修改的页面来完成写操作,而子进程在fork时仍然读取整个父进程的内存快照。所以,一般来说,fork不会消耗太多时间。可以执行infostats命令获取latestforkusec指标,该指标表示最近一次Redis的fork操作耗时。如果耗时较长,比如超过1秒,则需要进行优化调整。>redis-cli-c-p7000info|grep-wlatest_fork_useclatest_fork_usec:315当我们开启AOF持久化功能时,一般每秒使用一次文件刷刷方式,后台线程每秒对AOF文件进行一次fsync操作。当硬盘有压力时,fsync操作需要等到写入完成。如果主线程发现距离上次fsync成功已经超过2秒,为了数据安全,它会阻塞直到后台线程完成fsync操作。这种阻塞行为主要是硬盘压力造成的。您可以查看Redis日志来确定这种情况。当出现这种阻塞行为时,会打印如下日志:AsynchronousAOFfsyncisttakingtoolong(diskisbusy).\WritingtheAOFbufferwithoutwaitingforfsynctocomplete,\thismayslowdownRedis。您还可以检查信息持久性统计信息中的aofdelayedfsync指标,每次fdatasync阻塞主线程时都会累积。>infopersistenceloading:0aof_pending_bio_fsync:0aof_delayed_fsync:0Memoryswap内存交换(swap)对Redis来说是非常致命的。Redis保证高性能的一个重要前提是所有数据都在内存中。如果操作系统将Redis使用的部分内存换出到硬盘上,由于内存和硬盘的读写速度相差几个数量级,换出后Redis的性能会急剧下降。识别Redis内存swap的检查方法如下:>redis-cli-p6383infoserver|grepprocess_id#查询redis进程号>cat/proc/4476/smaps|grepSwap#查询内存swap大小Swap:0kBSwap:4kBSwap:0kBSwap:0kB它是0KB或者个别是4KB,属于正常现象,说明Redis进程的内存还没有被交换。有很多方法可以避免内存交换的发生。例如:确保机器有足够的可用内存。确保所有Redis实例设置最大可用内存(maxmemory),防止Redis内存在极端情况下不可控增长。降低系统的swap优先级,比如echo10>/proc/sys/vm/swappiness。