浅谈Redis缓冲区机制

目录
  • Redis缓冲区机制
  • 客户端缓冲机制
  • 应对输入缓冲区溢出
    • 查看输入缓冲区信息
    • 如何解决输入缓冲区溢出
  • 应对输出缓冲区溢出
    • Monitor命令的执行
    • 输出缓冲区设置不合理
  • 主从集群中的缓冲区
    • 复制缓冲区
    • 复制缓冲区溢出
    • 复制积压缓冲区
    • 解决复制积压缓冲区溢出

Redis缓冲区机制

Redis中的缓冲区机制就是为了平衡客户端发送命令和服务端处理命令的速度差异,如果客户端写入过快或者服务端读取过慢这就会导致缓冲区溢出,缓冲区一旦溢出将引发一系列的性能问题,下面我们详细聊聊。

客户端缓冲机制

Redis为每一个客户端都分配了一个输入缓冲区和输出缓冲区,输入缓冲区会把客户端的请求命令暂存起来,Redis主线程会从缓冲区中获取命令,当Redis处理完命令后会将结果写入到输出缓冲区中,通过输出缓冲区返回给客户端,如下所示

应对输入缓冲区溢出

输入缓冲区溢出一般就是两种情况

  • 写入数据过快,或者写入bigkey的数据占满数据缓冲区。
  • 服务端处理数据过慢,一般是主线程被阻塞无法正常响应客户端请求。

查看输入缓冲区信息

我们可以采用client list查看输入缓冲区的具体信息

127.0.0.1:6379> client list
id=13 addr=127.0.0.1:50484 fd=7 name= age=1136 idle=1 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client user=default
id=14 addr=127.0.0.1:50486 fd=8 name= age=1114 idle=6 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=client user=default

每连接上一个客户端就会多一条输入缓冲区信息,上面命令是我本地连接两个客户端的结果,我们查看缓冲区主要关注内存相关的两个参数

  • qbuf:缓冲区已经使用的长度(字节为单位,0表示没有分配缓冲区)。
  • qbuf-free:缓冲区剩余空闲空间(字节为单位),上面一个客户端的qbuf=26,空闲缓冲区qbuf-free=32742,那么分配内存总大小为26+32742=32768字节也就是32KB。

如果输入缓冲区信息中的qbuf-free很小并且qbuf很大时就需要注意了,这时输入缓冲区可能已经快溢出了,如果此时还有大量请求写入输入缓冲区,Redis的解决办法就是关闭和这个客户端的连接,那么业务数据将无法正常存取。

而且还有一个问题就是输入缓冲区是每一个客户端都会存在,那么当所有客户端的输入缓冲区内存总和超过了maxmemory配置,那么将引发内存淘汰,部分淘汰的数据再次访问需要从后台数据库获取,获取的耗时肯定比Redis直接读取慢的多,所以这也是Redis产生延迟的一个原因。

如何解决输入缓冲区溢出

输入缓冲区溢出本质就是缓冲区的容量不够,所以第一个思路就是扩大输入缓冲区的大小,很不幸Redis没有提供给我们修改输入缓冲区大小的配置,Redis要求每个客户端的输入缓冲区最大不能超过1G,注意是每个客户端!!!,如果客户端的输入缓冲区超过一个1G将关闭客户端连接,所以这个是行不通的。

那就只能从客户端发送数据的大小以及服务端处理命令的速度,客户端需要避免bigkey的写入bigkey的劣势太多一般都需要拆分,第二服务端的命令处理速度这个一般依赖于主线程是否阻塞,需要尽量的避免一些阻塞操作如AOF文件重写,键值删除,fork线程等等。

应对输出缓冲区溢出

对于服务端而言,客户端的输入信息通常都是不可预测的,但是输出信息大多可以预测,如Set命令返回信息只是一个简单的OK,又如一些报错信息,Redis为这些不变的返回信息分配了16KB的固定缓冲空间,也就是说输出缓冲区分为两个部分一部分是输出缓冲区固定返回信息,一部分是可变的返回信息。

输出缓冲区溢出分为三种情况

  • 输出bigkey等容量大的键值。
  • 客户端执行Monitor命令,监控Redis执行。
  • 缓冲区设置不合理。

bigkey是老生常谈的一个问题,当服务端输出一个bigkey或者keys这类命令,对输出缓冲区的考验是非常大的,因为查询的一瞬间会占据输入缓冲区大量的内存空间。

Monitor命令的执行

Monitor命令一般是一个debug命令,用来监控Redis的具体执行情况,能够返回服务器处理的每一个命令。

127.0.0.1:6379> monitor
OK
1652184977.609761 [0 127.0.0.1:50484] "get" "name"
1652185391.529292 [0 127.0.0.1:50484] "set" "test" "lisi"
......

一直运行monitor将一直占据输出缓冲区,也就是说占据时间越长,越容易造成输出缓冲区的溢出,所以Monitor命令仅仅只适用于调试环境,不能在生产上执行这些命令。

输出缓冲区设置不合理

输入缓冲区的大小不能设置,但是输出缓冲区的是可以设置的我们可以通过配置项client-output-buffer-limit来设置,设置的内容就是两部分

  • 缓冲区的内存大小,当超过缓冲区配置的大小,服务端会关闭和客户端的连接。
  • 持续写入的时间限制和持续写入的容量限制,当超过持续写入时间限制和容量限制,服务端也会强制关闭和客户端的连接。

客户端种类

在聊缓冲区配置时,我们需要先了解下客户端的种类,本文中强调的客户端并不是单纯指通过命令./redis-cli -c -h 127.0.0.1 -p 6379去连接Redis服务器这类客户端称为常规客户端,我们还有通过消息订阅Redis频道的客户端,还有一种最为特殊的主从同步,从节点也是一个特殊的客户端称为从节点客户端。

配置项client-output-buffer-limit也是针对这三种,给出了不一样的配置,如下所示

## 普通客户端配置
client-output-buffer-limit normal 0 0 0
## 从节点客户端配置
client-output-buffer-limit replica 256mb 64mb 60
## 消息订阅频道的客户端
client-output-buffer-limit pubsub 32mb 8mb 60

######################配置解释######################
## 第一个参数:代表分配给客户端的缓存大小,为0代表没有限制
## 第二个参数:表示持续写入的最大内存,为0代表没有限制
## 第三个参数:表示持续写入的最长时间,为0代表没有限制

普通客户端设置

普通客户端就是传输的一些普通的指令,一个指令发送完需要等待其返回后才会发送下一个指令,也就是说只要不是返回的bigkey数据,占用输出缓冲区的内存就极少,能够立即发送给客户端响应,所以一般正常客户端默认配置都是0,也就是不限制。

消息订阅频道客户端

当订阅频道产生消息后,会将消息通过输出缓冲区发送给客户端,这种属于非阻塞的方式,一瞬间可能有多个指令到达,所以需要指定缓冲区大小。

如何解决输出缓冲区溢出

到这里其实我们已经能够得到输出缓冲区溢出的解决方案了

  • bigkey应当避免使用。
  • Monitor命令只在调试的时候使用,不能应用到生产。
  • 合理设置输出缓冲区上限、持续写入时间上限以及持续写入内存容量上限。

主从集群中的缓冲区

除了输入缓冲区和输出缓冲区外在主从集群场景下还存在两种缓冲区,我们称为复制缓冲区和复制积压缓冲区,这两个缓冲区的溢出和输入输出缓冲区稍有不同。

复制缓冲区

复制缓冲区这个名词看着很陌生,但是我们之前在聊主从同步时讲过,主从全量同步期间从节点会加载主节点的RDB文件,这时主节点同样还能写入数据,但是从节点在加载RDB文件没办法实时同步,所以Redis就为每一个从节点开辟了一片空间,用来存放主从全量同步期间产生的操作命令,这就是replication buffer,也就是复制缓冲区。

复制缓冲区溢出

复制缓冲区什么时候会溢出呢?

  • 当从节点在加载RDB文件这个过程中如果存在大量的写操作就会造成复制缓冲区内存溢出。
  • 从节点加载RDB文件的时间过长。

发生溢出后,主节点会关闭与从节点的连接,导致全量同步失败。

解决复制缓冲区溢出

控制主节点实例的大小,减小生成的RDB文件,这样就能减少从节点加载RDB文件的时间,减小复制缓冲区的压力。

从节点其本质就是主节点的特殊客户端,所以使用的是输出缓冲区(也就是指replication buffer),可以设置client-output-buffer-limit replica 256mb 64mb 60扩大缓冲区大小。

注意:主节点上的复制缓冲区会为每一个从节点分配一个,那么从节点的数量过多即使每个从节点没有达到maxmemory,但累加的结果也会给主节点带来内存压力

复制积压缓冲区

复制积压缓冲区溢出

主从集群在写操作时会将操作写入复制缓冲区和复制积压缓冲区中,一旦网络发送故障后恢复连接,在2.8版本之前主从节点会进行全量同步开销非常大,所以2.8版本后还是采用了增量同步,仅仅将网络断开这段时间的操作同步给从节点,所以在网络恢复连接后从节点会将自己的复制偏移量slave_repl_offset发送给主节点,主节点将自身的写入偏移量master_repl_offset和slave_repl_offset在复制积压缓冲区中做对比得到网络断连期间的操作。

复制积压缓冲区又叫repl_backlog_buffer,是一个环形缓冲区,同步示意图如下。

复制积压缓冲区溢出其实也就是因为复制积压缓冲区是一个有限环形结构,一般主节点写入偏移量要大于从节点的读取偏移量,但如果写入偏移量覆盖了从节点的读取偏移量这就引发了复制积压缓冲区溢出。

解决复制积压缓冲区溢出

一般是调整repl_backlog_size这个参数的大小,扩大复制积压缓冲区的大小,减小主节点写入偏移量覆盖从节点读取偏移量的风险。

到此这篇关于浅谈Redis缓冲区机制的文章就介绍到这了,更多相关Redis缓冲区内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 浅谈Redis的异步机制

    目录 前言 一.Redis 的阻塞点 4 类交互对象和具体的操作之间的关系: 切片集群实例交互时的阻塞点 二.可以异步执行的阻塞点 三.异步的子线程机制 总结 前言 命令操作.系统配置.关键机制.硬件配置等会影响 Redis 的性能,不仅要知道具体的机制,尽可能避免性能异常的情况出现,还要提前准备好应对异常的方案. Redis 内部的阻塞式操作: CPU 核和 NUMA 架构的影响: Redis 关键系统配置: Redis 内存碎片: Redis 缓冲区. 一.Redis 的阻塞点 和 Redi

  • Java手动实现Redis的LRU缓存机制

    前言 最近在逛博客的时候看到了有关Redis方面的面试题,其中提到了Redis在内存达到最大限制的时候会使用LRU等淘汰机制,然后找了这方面的一些资料与大家分享一下. LRU总体大概是这样的,最近使用的放在前面,最近没用的放在后面,如果来了一个新的数,此时内存满了,就需要把旧的数淘汰,那为了方便移动数据,肯定就得使用链表类似的数据结构,再加上要判断这条数据是不是最新的或者最旧的那么应该也要使用hashmap等key-value形式的数据结构. 第一种实现(使用LinkedHashMap) pub

  • 浅谈Redis中的自动过期机制

    目录 Redis中的自动过期机制 一.使用Redis Key自动过期机制 二.SpringBoot整合key失效监听 Redis中的自动过期机制 实现需求:处理订单过期自动取消,比如下单30分钟未支付自动更改订单状态 1.使用Redis Key自动过期出发事件通知2.使用定时任务30分钟后检查3.按照每分钟轮训检查 CREATE TABLE `order_number` ( `id` int(11) NOT NULL AUTO_INCREMENT, `order_name` varchar(25

  • 浅谈redis的过期时间设置和过期删除机制

    目录 一:设置过期时间 二:保存过期时间 三:移除过期时间 四:计算并返回剩余生存时间 五:过期键的删除策略 六:redis使用的策略 一:设置过期时间 redis有四种命令可以用于设置键的生存时间和过期时间: EXPIRE <KEY> <TTL> : 将键的生存时间设为 ttl 秒 PEXPIRE <KEY> <TTL> :将键的生存时间设为 ttl 毫秒 EXPIREAT <KEY> <timestamp> :将键的过期时间设为

  • redis缓存存储Session原理机制

    目录 基于 Redis 存储 Session 首先安装 redis 存储引擎的包 设置session过期时间 分布式获取Session:(redis) 基于 Redis 存储 Session 如果我们想将 session 数据保存到 redis 中,只要将 session 的存储引擎改成 redis 即可. 使用 redis 作为存储引擎的例子: 首先安装 redis 存储引擎的包 go get github.com/gin-contrib/sessions/redis // 初始化基于 red

  • redis 过期策略及内存回收机制解析

    目录 1. 过期策略 1.1 过期的 key 集合 1.2 定时扫描策略 1.3 从库的过期策略 2. 懒惰删除 2.1 异步线程 2.2 flush 2.3 异步队列 2.4 AOF Sync很慢的问题 2.5 更多异步删除点 3. 过期淘汰配置 4. LRU 算法 4.1 近似 LRU 算法 5. LRU 5.1 LRU 模式 5.2 LFU 模式 redis作为缓存的场景下,内存淘汰策略决定的redis的内存使用效率.考虑到这个很多大厂给出的"送分题",但一般人很少能讲清楚,除非

  • 浅谈java如何实现Redis的LRU缓存机制

    目录 LRU概述 使用LinkedHashMap实现 使用LinkedHashMap简单方法实现 双链表+hashmap LRU概述 最近使用的放在前面,最近没用的放在后面,如果来了一个新的数,此时内存满了,就需要把旧的数淘汰,那为了方便移动数据,肯定就得使用链表类似的数据结构,再加上要判断这条数据是不是最新的或者最旧的那么应该也要使用hashmap等key-value形式的数据结构. 使用LinkedHashMap实现 package thread; import java.util.Link

  • 浅谈Redis缓冲区机制

    目录 Redis缓冲区机制 客户端缓冲机制 应对输入缓冲区溢出 查看输入缓冲区信息 如何解决输入缓冲区溢出 应对输出缓冲区溢出 Monitor命令的执行 输出缓冲区设置不合理 主从集群中的缓冲区 复制缓冲区 复制缓冲区溢出 复制积压缓冲区 解决复制积压缓冲区溢出 Redis缓冲区机制 Redis中的缓冲区机制就是为了平衡客户端发送命令和服务端处理命令的速度差异,如果客户端写入过快或者服务端读取过慢这就会导致缓冲区溢出,缓冲区一旦溢出将引发一系列的性能问题,下面我们详细聊聊. 客户端缓冲机制 Re

  • 浅谈Redis 中的过期删除策略和内存淘汰机制

    目录 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 内存淘汰触发的最大内存 有哪些内存淘汰策略 内存淘汰算法 LRU LFU 为什么数据删除后内存占用还是很高 内存碎片如何产生 碎片率的意义 如何清理内存碎片 总结 参考 前言 Redis 中的 key 设置一个过期时间,在过期时间到的时候,Redis 是如何清除这个 key 的呢? 这来分析下 Redis 中的过期删除策略和内存淘

  • 浅谈Redis 中的过期删除策略和内存淘汰机制

    目录 前言 Redis 中 key 的过期删除策略 1.定时删除 2.惰性删除 3.定期删除 Redis 中过期删除策略 从库是否会脏读主库创建的过期键 内存淘汰机制 内存淘汰触发的最大内存 有哪些内存淘汰策略 内存淘汰算法 LRU LFU 为什么数据删除后内存占用还是很高 内存碎片如何产生 碎片率的意义 如何清理内存碎片 总结 参考 前言 Redis 中的 key 设置一个过期时间,在过期时间到的时候,Redis 是如何清除这个 key 的呢? 这来分析下 Redis 中的过期删除策略和内存淘

  • 浅谈Redis缓存有哪些淘汰策略

    目录 Redis过期策略 定时删除 惰性删除 定期删除 Redis的内存淘汰机制 LRU和LFU的区别 LRU LFU Redis重启如何恢复数据呢? 总结 Redis过期策略 我们首先来了解一下Redis的内存淘汰机制. 定时删除 概述     redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除.注意这里是随机抽取的.为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会

  • 浅谈Redis跟MySQL的双写问题解决方案

    目录 写在前面 三种读写缓存策略 Cache-AsidePattern(旁路缓存模式) Read-Through/Write-Through(读写穿透) WriteBehindPattern(异步缓存写入) 旁路缓存模式解析 CacheAsidePattern的一些疑问 CacheAsidePattern的缺陷 项目中有遇到这个问题,跟MySQL中的数据不一致,研究一番发现这里面细节并不简单,特此记录一下. 写在前面 严格意义上任何非原子操作都不可能保证一致性,除非用阻塞读写实现强一致性,所以缓

  • 浅谈Redis高并发缓存架构性能优化实战

    目录 场景1: 中小型公司Redis缓存架构以及线上问题实战 场景2: 大厂线上大规模商品缓存数据冷热分离实战 场景3: 基于DCL机制解决热点缓存并发重建问题实战 场景4: 突发性热点缓存重建导致系统压力暴增 场景5: 解决大规模缓存击穿导致线上数据库压力暴增 场景6: 黑客工资导致缓存穿透线上数据库宕机 场景7: 大V直播带货导致线上商品系统崩溃原因分析 场景8: Redis分布式锁解决缓存与数据库双写不一致问题实战 场景9: 大促压力暴增导致分布式锁串行争用问题优化 场景10: 利用多级缓

  • 浅谈Redis的事件驱动模型

    Redis 作为一个 Client-Server 架构的数据库,其源码中少不了用来实现网络通信的部分.而你应该也清楚,通常系统实现网络通信的基本方法是使用Socket编程模型,,包括创建 Socket.监听端口.处理连接请求和读写请求.但是,由于基本的 Socket 编程模型一次只能处理一个客户端连接上的请求,所以当要处理高并发请求时,一种方案就是使用多线程,让每个线程负责处理一个客户端的请求. 而 Redis 负责客户端请求解析和处理的线程只有一个,那么如果直接采用基本 Socket 模型,就

随机推荐