5

面试官:Redis中的缓冲区了解吗 - 七淅在学Java

 2 years ago
source link: https://www.cnblogs.com/czd-xi/p/16061186.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

面试官:Redis中的缓冲区了解吗

hello 大家好,我是七淅(xī)。

Redis 大家肯定不陌生,但在使用层面看不到的地方,就容易被忽略。今天想和大家分享的内容是 Redis 各个缓冲区的作用、溢出的后果及优化方向

在开始正文前,想多叨叨几句。不管是 Redis 还是其他中间件,底层很多原理都是相似的,设计思想都是通用的。

大家以后如果在学什么新框架/组件,可以尽量和已经学过的知识点进行联想,这样会更容易理解点的,不至于说死记硬背。

比如现在说到的缓冲区,它的目的是什么呢?

无它,为了性能。

要么缓存数据,提高响应速度。比如 MySQL 中有个 change buffer

要么担心消费者速度跟不上生产,怕数据丢失。所以需要把生产数据先暂存起来。Redis 的缓冲区就是这个作用。

另外,消费者速度跟不上,如果是同步处理的话,那是不是也会拖慢生产者,所以这里其实也是在保证生产者的速度。

可能有的读者会说:扯淡,消费者都跟不上了,生产者再快有什么用?

其实有没有一种可能,生产者根本不关心消费者什么时候用呢?前者是负责把后者需要的东西处理好给它就完事了。生产者很忙,还有其他一大堆数据要处理,不能慢慢等消费者同步消费完才去做其他事情。

好像开头扩展得有点多,我收一收,下面会详细说到。有疑问的小伙伴请上车,七淅正式发车了。

1. 各缓冲区

首先 Redis 有什么缓冲区呢?

一共 4 个:

  • 客户端输入缓冲区
  • 客户端输出缓冲区
  • 复制缓冲区
  • 复制积压缓冲区

2. 客户端输入缓冲区

服务器端会给每个连接的客户端都设置了一个输入缓冲区。

2.1 作用

暂存请求数据。

输入缓冲区会先把客户端发送过来的命令暂存起来,Redis 主线程再从输入缓冲区中读取命令,进行处理。

为了避免客户端和服务器端的请求发送和处理速度不匹配,这点和等下要说的输出缓冲区是一样的。

2.2 溢出场景

首先缓冲区是一块固定大小的内存区域,如果要把这个地方填满的话,那 Redis 会直接把客户端连接关闭。

保护自己嘛,你客户端挂了总比我服务端挂了好,服务端一挂就是所有客户端都没用了。

那填满缓冲区就有 2 个情况了:

  1. 要么一下子填满
  2. 要么生产速度大于消费速度,慢慢被填满

那么把上述原理对应到 Redis 的场景。

一下子填满的情况可以是往 Redis 里写大量数据,百万千万数量级那种。

另一个情况可以是 Redis 服务端因执行耗时操作,阻塞住了,导致没法消费输入缓冲区数据。

2.3 优化

对应上面 2 个溢出场景,优化方向很自然就有了。

一下子填满的情况,是不是可以考虑不要一下子写这么多数据,能否拆下数据(其实一下子写大量数据本身就不合理哈)

另外,是否可以调高缓冲区大小呢?

这个其实是不行的哈,因为没有可以设置的地方,目前服务端默认为每个客户端输入缓冲区分配的大小是 1GB。

那轮到第 2 个溢出场景:两边处理速度不一致。

正常来说,服务端不应该出现长时间阻塞,所以需要看看是什么原因导致的阻塞,解决到就好了。

3. 客户端输出缓冲区

同输入缓冲区,服务器端也会给每个连接的客户端都设置了一个输出缓冲区。

3.1 作用

同上,也是暂存请求数据。

这个地方其实我在文章开头说的,生产者不关心消费者什么时候用,只负责把消费者之前请求的东西处理好就完事了。

可能有点抽象,我是这么理解的,如果有不妥的地方可以留言纠正我一下

服务端一般都会和多个客户端连接,加上 redis 网络通信模块是单线程的(即使是新版本支持多线程也一样)

假如没有输出缓冲区会发生什么事呢?

服务端处理了很多客户端 A 的请求,需要经过网络这一耗时操作,返回给客户端 A。在这个过程中,客户端 B 的请求一直得不到服务端处理和响应,这样吞吐量就上不去了。

有了缓冲区之后,至少能解放服务端,让它去处理客户端 B 的请求。

3.2 溢出场景

这里也是同输入缓冲区,我就不啰嗦了,溢出的话服务端也会关闭客户端连接。

  1. 服务器端返回了大量数据,一下子填满了
  2. 返回数据的速度太快,比如执行 MONITOR 命令,它会持续输出监测到的各个命令操作
  3. 缓冲区大小设置得不合理。

3.3 优化

类似的,不要一下子读大量数据;不持续在线上执行 MONITOR 命令。

而输出缓冲区的大小是可以通过 client-output-buffer-limit 来设置的。

但是一般来说,我们都不用改,因为默认情况就够了,这里了解下就好。

值得说一点的是,Redis 发布订阅的消息也是在该缓冲区中,可以用 client-output-buffer-limit pubsub 8mb 2mb 60 来限制大小。

  • pubsub 表示对订阅客户端进行设置。换成 normal 则表示当前设置的是普通客户端
  • 整个配置的含义是:实际占用的缓冲区大小要超过 8MB,或者连续 60 秒内对输出缓冲区的写入量超过 2MB 的话,服务端就会关闭客户端连接。

4. 复制缓冲区

温馨提示下,如果对 Redis 同步/复制不了解的读者,比如不知道全量/增量复制,建议可以看下我这篇文章:一文让你明白Redis主从同步

下面回到正题哈。

有复制肯定有主从,而主从间的数据复制包括全量复制和增量复制两种。

全量复制是同步所有数据,而增量复制只会把主从库网络断连期间主库收到的命令,同步给从库。

4.1 作用

暂存数据。

主节点上会为每个从节点都维护一个复制缓冲区。

在全量复制时,主节点在向从节点传输 RDB 文件的同时,会继续接收客户端发送的写命令请求,并保存在复制缓冲区中,等 RDB 文件传输完成后,再发送给从节点去执行。

4.2 溢出场景

从节点接收和加载 RDB 较慢,同时主节点接收到了大量的写命令,写命令在复制缓冲区中就会越积越多,最后就会溢出。

一旦溢出,主节点会直接关闭和从节点进行复制操作的连接,导致全量复制失败

4.3 优化

可以控制主节点数据量在 2~4GB(仅供参考),这样可以让全量同步执行得更快些,避免复制缓冲区累积过多命令

也可以调整缓冲区大小,还是之前的 client-output-buffer-limit 参数。

比如: config set client-output-buffer-limit slave 512mb 128mb 60

  • slave 参数表明该配置项是针对复制缓冲区的.
  • 整个配置的含义是:实际占用的缓冲区大小要超过 512MB,或者连续 60 秒内对输出缓冲区的写入量超过 128MB 的话,服务端就会关闭同步连接。

5. 复制积压缓冲区

这个是在新增复制用到的缓冲区。

具体介绍还是推荐看上面提到的文章哈,写到这里也 2k+ 字了,顶不住啦。

5.1 作用

暂存数据。

从节点意外断开连接后重连,可从该缓冲区同步期间没同步到的数据。

5.2 溢出场景

不会溢出。(想不到吧.jpg)

该缓冲区本质是一个固定长度,先进先出的队列,默认 1MB。

所以当队列被占满,不是报错,也不像上面几个缓冲区直接关闭连接。而是覆盖最早进入队列的数据。

因此,如果有从节点还没有同步这些旧命令数据,就会导致主从节点重新进行全量复制,而不是增量复制。

PS:全量复制性能开销远大于增量复制

5.3 优化

调整复制积压缓冲区的大小,参数是:repl_backlog_size

原创不易,如果觉得文章不错,希望能关注下我


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK