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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试官:Redis中的缓冲区了解吗-相关的知识,希望对你有一定的参考价值。

参考技术A

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

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

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

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

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

无它,为了性能。

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

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

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

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

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

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

首先 Redis 有什么缓冲区呢?

一共 4 个:

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

暂存请求数据。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

下面回到正题哈。

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

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

暂存数据。

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

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

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

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

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

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

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

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

暂存数据。

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

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

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

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

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

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

面试官:Redis的共享对象池了解吗?

我正在面试间里焦急地等待着,突然听到了门外的脚步声,随即门被打开,穿着干净满脸清秀的青年走了进来,一股男士香水的淡香扑面而来。

面试官:“平时在工作中用过Redis吗?”

我:“用的比较多。”我心中暗喜,Redis我熟啊,什么五种数据类型、两种持久化方式倒背如流啊。

面试官:“Redis的共享对象池了解吗?”

“这个。。没有太深入了解。”我支支吾吾的说到,手心已经冒出冷汗。

面试官:“回去等消息吧。”

这句话说的干净利落,然后就没有然后了。失败是成功的妈妈,我不气馁,决定马上恶补一下。

共享对象池

创建大量重复的整数类型势必会耗费大量内存,所以在Redis内部维护了一个从0到9999的整数对象池,这就是共享对象池

为了验证和理解,我们使用object refcount命令查看一下对象引用数,效果如下:

127.0.0.1:6379> set one-more-num1 404
OK
127.0.0.1:6379> object refcount one-more-num1
(integer) 2
172.24.130.22:6379> set one-more-num2 404
OK
127.0.0.1:6379> object refcount one-more-num2
(integer) 3

设置one-more-num1为404后,直接使用共享池中的整数对象,所以引用数为2(另外一个引用在对象池上);再设置one-more-num2为404后,引用数变成了3。

不过需要注意的是:当设置最大内存值(maxmemory)并且启用LRU相关淘汰策略(如:volatile-lru、allkeys-lru)时,共享对象池将会被禁止使用。

为什么没有字符串对象池?

共享对象池中一个关键操作是判断对象是否相等。

Redis中只有整数类型的对象池,是因为整数的比较算法的时间复杂度是O(1),也只保留了10000个整数为了防止对象池的过度浪费。

相对而言,字符串的比较算法的时间复杂度是O(n),特别是长字符串的比较更加消耗性能。

而且,整数类型被重复使用的概率很大,字符串被重复使用的概率相比就会小很多很多,所以在Redis中只用整数类型的对象共享池。


面试官你等着瞧吧,今天你对我爱答不理,明天我让你高攀不起,哈哈哈。。。

参考文献: 《Redis设计与实现》 《Redis开发与运维》 《Redis 深度历险:核心原理与应用实践》


竟然已经看到这里了,你我定是有缘人,留下你的点赞关注,他日必成大器。

以上是关于面试官:Redis中的缓冲区了解吗-的主要内容,如果未能解决你的问题,请参考以下文章

面试官:Redis的共享对象池了解吗?

面试官:MySQL中InnoDB了解吗?一文深入分析如何避坑

面试干货7——刁钻面试官:关于redis,你都了解什么?

面试干货7——刁钻面试官:关于redis,你都了解什么?

面试官:你对Redis缓存了解吗?面对这11道面试题你是否有很多问号?

面试官:你对Redis缓存了解吗?面对这11道面试题你是否有很多问号?