0

Redis内存——三个重要的缓冲区

尽于生 2021-06-21
713
缓冲区(buffer),是内存空间的一部分。也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区。
缓冲区概念最初是操作系统为了缓和 CPU 与 I/O 设备速度不匹配的矛盾,提高 CPU 和 I/O 设备的并行性而引入的。
对于高速设备与低速设备的不匹配,势必会让高速设备花时间等待低速设备。有了缓冲区的概念就可以很好的解决这个问题。缓冲区也是生产者消费者模式的重要体现。
缓冲区的优势主要有两个:
1.可以缓和 CPU 与 I/O 设备速度不匹配的矛盾,数据可以直接送往缓冲区,高速设备不用再等待低速设备,提高了计算机的效率。例如:我们使用打印机打印文档,由于打印机的打印速度相对较慢,我们先把文档输出到打印机相应的缓冲区,打印机再自行逐步打印,这时我们的CPU可以处理别的事情。
2.可以减少数据的处理次数,如果操作是在写磁盘就可以减少磁盘的读写次数,而如果是CPU来接收远端通信的命令,缓冲区就可以减少CPU的中断次数。
缓冲区固然可以解决很多问题,但如果使用不当同时也会带来很严重的问题,最主要的就是内存溢出!我们知道缓冲区是内存空间的一部分,那么就一定有限的,既然有大小的限制,那么就很有可能发生内存溢出。而常见的导致内存溢出的原因有两个
  1. 如果往里面写入数据的速度持续地大于从里面读取数据的速度,就会导致缓冲区需要越来越多的内存来暂存数据。当缓冲区占用的内存超出了设定的上限阈值时,就会出现缓冲区溢出。
  2. 如果存入的数据过大直接超过了设定的上限阈值时,也会出现缓冲区溢出。

 Redis三个重要缓冲区


在Redis中,主要有三个场景用到了缓冲区的概念。Redis 是 client-server 架构,所以,缓冲区在 Redis 中的一个主要应用场景,就是在客户端和服务器端之间进行通信时,用来暂存客户端发送的命令数据,或者是服务器端返回给客户端的数据结果。此外,在主从节点间进行数据同步时,Redis使用缓冲区来暂存主节点接收的写命令和数据。在Redis进行AOF持久化的时候Redis为了避免频繁写磁盘同样用到了缓冲区的概念。
上面我们提到缓冲区如果利用不好就会出现内存溢出,在Redis中同样如此,一旦缓冲区占用的内存超出了设定的上限阈值时,就会出现缓冲区溢出。而如果不给这个缓冲区设置上限,那么随着缓冲区的数据越来越多,其占用内存空间的越来越大,就会导致Redis性能变慢,而一旦耗尽了 Redis 实例所在机器的可用内存,就会导致 Redis 实例崩溃。
因此我们必须深入的理解缓冲区在Redis各个场景的使用,才能真正达到提高Redis效率,避免内存溢出的目的。

2.1 客户端缓冲区


客户端缓冲区又有两个,输入缓冲区和输出缓冲区,都是为了解决客户端和服务器端的请求发送和处理速度不匹配所设置的。
输入缓冲区会先暂存客户端发送过来的命令,Redis 主线程从输入缓冲区中读取命令,进行处理。当 Redis 主线程处理完数据后,会把结果写入到输出缓冲区,再从输出缓冲区返回给客户端。
输入缓冲区暂存的是客户端发来的命令,其常见的溢出原因有两个:
  1. 写入了BigKey,如一次性写入了百万级别的哈希或集合数据,超过了缓冲区的大小
  2. 服务端处理请求的速度过慢导致阻塞,无法及时处理请求,使得客户端发送的请求在缓冲区内越积越多。
输出缓冲区暂存的是 Redis 主线程要返回给客户端的数据。这个数据,既有简单且大小固定的 OK 响应(例如,执行 SET 命令)或报错信息,也有大小不固定的、包含具体数据的执行结果(例如,执行 HGET 命令)
输出缓冲区常见的溢出原因有三种:
  1. 返回BigKey的大量结果
  2. 执行了某些不合理的命令
  3. 缓冲区大小设置不合理
从输入和输出缓冲区常见导致溢出的原因来看,BigKey是最可能导致溢出的原因,因此我们应该尽量避免使用BigKey。对于输入缓冲区,因为没有办法改变其大小(默认每个客户端1G),我们只能通过控制命令的发送和处理速度入手,尽量避免阻塞。
对于输出缓冲区则要避免一些返回大量结果的命令的使用如KEYS,MONITOR等,同时可以通过调整输出缓冲区的大小来避免溢出。

2.2 复制缓冲区


复制缓冲区是用于Redis主从节点之间复制时使用的。由于主从节点间的数据复制包括全量复制和增量复制两种。因此复制缓冲区也分为复制缓冲区和复制积压缓冲区两种。
复制缓冲区
在全量复制过程中,主节点在向从节点传输 RDB 文件的同时,会继续接收客户端发送的写命令请求。这些写命令就会先保存在复制缓冲区中,等 RDB 文件传输完成后,再发送给从节点去执行。主节点上会为每个从节点都维护一个复制缓冲区,来保证主从节点间的数据同步。
对于复制缓冲区,如果主库传输 RDB 文件以及从库加载 RDB 文件耗时长,同时主库接收的写命令操作较多,就会导致复制缓冲区被写满而溢出。
想要避免复制缓冲区溢出,一方面我们可以控制主节点保存的数据量大小,这样可以让RDB文件的传输以及从库加载时间变快,以避免复制缓冲区累积过多命令。也可以根据主节点的数据量大小、主节点的写负载压力和主节点本身的内存大小来更合理的设置复制缓冲区的大小来避免溢出,此外,由于主节点会为每一个从节点设置一个复制缓冲区,如果集群中的从节点数非常多的话,主节点的内存开销就会非常大,因此我们应该尽量避免一个主节点有过多的从节点。
复制积压缓冲区


增量复制时,主节点和从节点进行常规同步时,会把写命令也暂存在复制积压缓冲区中。如果从节点和主节点间发生了网络断连,等从节点再次连接后,可以从复制积压缓冲区中同步尚未复制的命令操作。
需要注意的是复制积压缓冲区是一个大小有限的环形缓冲区。当主节点把复制积压缓冲区写满后,会覆盖缓冲区中的旧命令数据。此时会造成主从节点的数据不一致。针对这个问题,一般的应对的方法是调大复制积压缓冲区的大小,这个大小的计算方式一般可以使用
    缓冲区大小=(主库写入命令速度 * 操作大小 - 主从库间网络传输命令速度 * 操作大小)* 2
    如果如果并发请求量非常大,调整缓冲区大小的方式还不能解决,那么可以考虑使用切片集群的方式解决

    2.3 AOF缓冲区


    AOF缓冲区是Redis在AOF持久化的所设置的缓冲区,关于AOF持久化我们前面已有文章总结过,这里我们再简单回顾下其中缓冲区的概念,AOF缓冲区也有两种AOF缓冲区和AOF重写缓冲区。
    AOF缓冲区
    我们都知道,即使是固态硬盘,它的读写速度也是与内存的读写速度相差很多的。AOF缓冲区就主要是Redis用来解决主进程执行命令速度与磁盘写入速度不同步所设置的,通过AOF缓冲区可以有效地避免频繁对硬盘进行读写,进而提升性能。Redis在AOF持久化的时候,会先把命令写入到AOF缓冲区,然后通过回写策略来写入硬盘AOF文件。
    AOF缓冲区的溢出可能与磁盘写入速度有关系,也可能与AOF回写策略有关系,当大量命令积压在AOF缓冲区,超过其设置阈值之后,就会导致缓冲区溢出,想要避免这个问题,我们可以通过调整回写策略,或者调整AOF缓冲区大小的方式来解决。
    AOF重写缓冲区
    AOF重写缓冲区是Redis在子进程进行AOF重写的时候,父进程接受了新的命令,此时会把命令写入AOF重写缓冲区,等到子进程重写完成后,把AOF重写缓冲区命令追加到新的AOF文件中。
    AOF重写缓冲区的溢出与AOF重写期间主进程所处理的命令数有关系,当AOF重写期间Redis主进程处理了大量的命令,这些命令都会写入AOF重写缓冲区,当超过设定阈值之后,就会导致溢出。避免AOF重写缓冲区的溢出我们也可以通过调整AOF重写缓冲区的大小来解决。

     总结

    这篇文章,我们总结了缓冲区的概念,分析了Redis的三个缓冲区,以及可能造成其溢出的原因和解决办法。对于缓冲区溢出其实主要有两种原因,一是缓冲区大小不够,二是消费者处理的速度太慢,而生产者生产的太快,导致大量内容积压在缓冲区,进而导致溢出。而解决方案就可以通过调整缓冲区的大小,或者调整生产者与消费者之间生产与处理消息的速度使其处于一个相对平衡的状态。
    对于Redis缓冲区而言多数场景我们可以通过调大缓冲区来解决,但这个调整是有限度的,我们不能一味的增加缓冲区的空间,当请求量过大,调整到一定大小之后,仍不能解决,此时我们就应该通过别的方式来解决问题,如切片集群等方案。


    「喜欢文章,快来给作者赞赏墨值吧」
    文章转载自尽于生,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论