一、定义
(1.1)Redis缓冲区存放什么
缓冲区 :用一块内存暂时存放数据和命令
Redis 是典型的C-S架构。所有操作指令通过客户端发送到服务端。
缓冲区作用于客户端和服务端之间:
(1)暂存客户端发送的命令
(2)暂存服务器端返回客户端的数据结果
(3)在主从节点架构中: 暂存主节点接收的写命令 以及 数据。
(1.2)Redis 缓冲区溢出
缓冲区的空间有限:
(1)当写入数据 持续大于 读取数据,缓冲区容量不足,导致缓冲区内存溢出 。(数据丢失)
(2)当读取数据 持续大于 写入数据,缓冲区响应数据不足,导致缓冲区内存溢出。(命令丢失)
(1.3)Redis 缓冲区不设置上限带来的后果
缓冲区是为了避免 请求丢失 和 避免数据丢失的。
Redis 缓冲区不设置上限,随着累积的数据和操作越来越多,缓冲区内存也越来越大。一旦耗尽redis实例所在机器的内存,就是导致Redis实例崩溃
(1.4)缓存区的组成
对于客户端通信来说,缓存区可以分为: 输出缓存区 & 输入缓存区
二、redis 客户端分类
普通客户端:常规和redis服务器进行读写交互 (阻塞式发送操作指令)
没发送完成一个请求,会等到请求结果返回以后,再发送下一个请求,称为阻塞式发送
订阅客户端:订阅了redis频道的订阅客户端 (非阻塞式发送操作指令)
订阅客户端,一旦订阅的Redis频道有消息了,服务器端就会通过输出缓冲区将数据发送给客户端。称为非阻塞式发送
三、缓存区的用途
(3.1)客户端通信
客户端通信 = 输入缓冲区 + 输出缓冲区
(3.1.1)输入缓冲区 -- 导致输入缓冲区溢出的情况
输入缓冲区主要存放客户端请求的命令
- 写入了bigkey (一下子写入多个百万级别的集合类型的数据)
- 服务器端处理请求过慢。(Redis主线程出现间歇性阻塞,无法及时处理正常发送的请求,导致客户端的请求在缓冲区累积越来越多)
(3.1.2)输入缓冲区 -- 查看缓冲区内存的使用情况
#root@redis-xxxxxx-xxxxxx:/data# redis-cli -a 密码 client list
qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
id=1347615892 addr=xx.xx.xx.x:x fd=1039 name= age=1031650 idle=3 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
id=1347615893 addr=xx.xx.xx.x:x fd=1044 name= age=1031650 idle=3 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
id=1347615894 addr=xx.xx.xx.x:x fd=1063 name= age=1031650 idle=3 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
id=1607916419 addr=xx.xx.xx.x:x fd=2206 name= age=426061 idle=26 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=ping
id=1791329487 addr=xx.xx.xx.x:x fd=2091 name= age=0 idle=0 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
#cmd : 表示客户端最新执行的命令
#qbuf :输入缓冲区已经使用的大小
# qbuf-free : 输入缓冲区尚未使用的大小 (PING 是 Redis 中最简单的命令,命令执行完成后,缓冲区会被立即释放)
(qbuf=26 qbuf-free=32742 : 表示缓冲区已经使用了26字节,这个例子中的client命令还可以使用32742字节,也就是32kb的缓冲区。
redis服务端为client这个命令分配的总输入缓冲区是 26+32742 ~ 32KB)
(3.1.3)一个客户端导致redis缓冲区溢出
通过 redis-cli client list 命令 (可以判断客户端输入缓冲区的内存占用情况)
若: qbuf很大 qbuf-free 很小 --》 需要注意,马上就会内存溢出了。
导致的后果:
redis实例 将客户端的连接关闭。业务程序无法正常的进行数据存取。
(3.1.4)多个客户端连接redis缓冲区溢出
Redis服务端被多个客户端连接。
多个客户端占用的内存总量 > redis 的maxmemory 配置项时,导致redis的输出缓冲区溢出。
输出缓冲区溢出导致的后果:
(1)输出缓冲区出现内存溢出,Redis会触发数据淘汰。一旦数据被淘汰出redis。再访问此部分数据,只能去后端数据库读取,降低业务应用的访问性能。
(2)如果多客户端都导致redis内存溢出国发,就会引起redis崩溃,给业务造成非常大的访问影响。
(3.1.5)解决Redis 输入缓存区溢出的问题
(1)此方法需要更改Redis代码:(不建议)
提高Redis客户端的输入缓冲区大小的上限阈值(代码中设置的1GB)。
(2)避免客户端写入bigkey
(3)各方面避免主线程的阻塞
(3.1.6)输出缓冲区的组成
输出缓冲区主要存放服务端返回的数据
(1)固定缓冲区空间:16KB,存放OK响应和出错信息
(2)动态缓冲区空间:暂存大小可变的响应数据结果
(3.1.7)导致输出缓冲区溢出的情况
- 服务端返回bigkey的大量的结果
- 执行了MONITOR命令 (查看文档最后的注释)
- 缓冲区大小设置的不合理
(3.1.8)解决输出缓冲区溢出的问题
(1)避免客户端的bigkey的建立
(2)避免Monitor命令的持续开启
(3)输出缓冲区设置大小 :(输入缓冲区不能设置)
设置缓冲区大小的上线阈值 + 设置输出缓冲区持续写入数据的数量上限阈值 + 设置持续写入时间的上限阈值
对于普通客户端 :
client-output-buffer-limit normal 0 0 0
normal : 表示普通客户端 (普通客户端是阻塞式操作,只要不是bigkey操作,一般不会出现溢出情况)
第一个0 :表示设置缓冲区的大小
第二个0 :表示缓冲区写入量限制
第三个0 :表示持续写入时间的限制
对于订阅客户端:
client-output-buffer-limit pubsub 8mb 2mb 60
pubsub : 表示订阅式客户端
8mb : 表示输出缓冲区大小上限是8MB
2mb 60 : 表示在连续的60秒内输出缓冲区的上限是2MB
当超出上述设置的上限时,服务器端就会关闭客户端连接
(3.2)主从复制
主从复制架构中: 主从数据的复制(全量/增量复制)
(3.2.1)复制缓冲区的溢出问题
复制缓冲区:
(1)主节点向从节点传输RDB全量内存快照文件时,主节点还在处理客户端的操作请求。其中写操作保存在复制缓冲区
(2)RDB文件传输完成以后,发送复制缓冲区的操作指令给从节点执行。
(3)主节点为每一个从节点都维护一个复制缓冲区,来保证主节点和从节点间数据的同步。
(所以,一主多从结构中都是 一主一从复制,而后从从复制)
复制缓冲区溢出对全量复制的影响:
复制缓冲区一旦溢出,主节点直接关闭和从节点的复制操作的连接。导致全量复制失败。
解决复制缓冲区内存溢出的问题:
(1)控制主节点的数据量的大小
(2)client-output-buffer-limit 配置项设置 : 合理设置复制缓冲区的大小
(3)主节点为每一个从节点都维护一个复制缓冲区 (从节点越多占用主节点的复制缓冲区越多)
尽量控制主节点连接从节点的个数
例如: config set client-output-buffer-limit slave 512MB 128MB 60
slave : 表示针对复制缓冲区
512MB : 表示复制缓冲区的上限
128MB 60 : 表示连续60秒写入量不能超过128MB
重点: 我们可以通过命令数据大小和应用的实际负载情况 判断复制缓冲区是否足够支撑写命令数据量
假设写数据是1KB
复制缓冲区可以支持 512MB / 1KB =512000 条写数据
在全量复制阶段,能承受的写命令是 128MB / 1kb = 128000 条写数据
(3.2.2)复制积压缓冲区的问题
复制积压缓冲区 : repl_backlog_buffer
主节点将接收的写命令交给从节点的时候,同时会将写命令写入到复制积压缓冲区。
一旦主从节点闪断,从再次和主链接,从阶段从复制积压缓冲区读取断连期间的写命令。保持主从的数据可靠性。
复制积压缓冲区 : 是环形缓冲区
复制积压缓冲区溢出问题 : 主节点将复制积压缓冲区写满数据的时候,会覆盖缓冲区的旧数据。从节点没有读取这部分旧数据就会造成数据丢失问题。
解决复制积压缓冲区溢出的问题:
调整repl_backlog_buffer 的大小。
注释区域:
(1)MONITOR命令是用来监测Redis执行的:持续的输出监测到的所有的命令操作。
持续的输出:所以占用输出缓冲区,并且越占越多,最后的结果就是溢出。