文章目录
- 【关于作者】
- 1.缓冲区
- 2.客户端输入和输出缓冲区
- 2.1.如何应对输入区溢出
- 2.2.如何应对输出缓冲区溢出
- 3.主从缓冲区
- 3.1.全量复制:
- 3.2.增量复制
- 4.总结
- 4.1缓冲区对Redis的影响
- 4.2.如何避免
【关于作者】
关于作者,目前在蚂蚁金服搬砖任职,在支付宝营销投放领域工作了多年,目前在专注于内存数据库相关的应用学习,如果你有任何技术交流或大厂内推及面试咨询,都可以从我的个人博客(https://0522-isniceday.top/)联系我
1.缓冲区
功能:用来暂时存储命令和数据,方式处理命令和数据的速度小于发送的数据从而导致数据的丢失和性能问题。往缓存区写入的速度大于读取的速度,会导致缓冲区占用的空间越来越大。当内存占用超过了阈值,则会导致缓冲区溢出,如果发生溢出,则会丢数据,并且占用的内存越来越大,耗尽了机器的内存则会导致redis实例崩溃
在redis中的应用场景:
- 暂存客户端发送的命令数据及服务端的返回结果-输入输出缓冲区
- 主从数据同步时,暂存主节点接收的写命令和数据-主从缓冲区
2.客户端输入和输出缓冲区
服务端给每个客户端都设置了一个数据输出缓冲区,其中作用如下图:
2.1.如何应对输入区溢出
(1)导致溢出的场景:
- 写入了bigkey,例如写入多个百万级别的集合数据
- 服务端处理请求速度过慢,例如主线程间歇性的阻塞,无法处理正常请求,导致客户端请求积累在缓冲区
(2)如何查看输入缓存区内存情况:
要查看和服务器端相连的每个客户端对输入缓冲区的使用情况,我们可以使用CLIENT LIST命令:
CLIENT LIST
id=5 addr=127.0.0.1:50487 fd=9 name= age=4 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
重点关注信息如下:
一类是客户信息,如:addr
一类是输入缓冲区相关的参数:
- cmd:客户端最新执行的命令
- qbuf:表示输入缓冲区已经使用的大小,上图使用了26字节的大小
- qbuf-free:输出缓冲区尚未使用的大小qbuf+qbuf-free=输入缓冲区总大小,这里为26+32742=32768字节=32KB
当多个客户端占用的内存总量超过了maxmemory
,则会触发redis进行数据淘汰,也可能导致redis占用内存过大,导致OOM,进而导致redis崩溃
(3)如何避免:
- 缓冲区调大
但是redis无法调整输入缓冲区大小,代码中设定为1GB,就是说每个客户端的输入缓存区最大为1GB - 数据命令的发送:避免客户端写入bigkey
- 避免redis主线程阻塞
2.2.如何应对输出缓冲区溢出
主线程返回给客户端的信息:
- 简单大小固定的OK响应或报错信息
- 大小不固定的执行结果(HGet等)
(1)导致溢出的场景:
- 返回bigkey的大量结果
- 执行了
MONITOR
命令 - 缓冲区大小设置不合理
MONITOR
命令:持续输出检查到的各个命令操作
MONITOR
OK
1600617456.437129 [0 127.0.0.1:50487] "COMMAND"
1600617477.289667 [0 127.0.0.1:50487] "info" "memory"
MONITOR命令主要用在调试环境中,不要在线上生产环境中持续使用MONITOR
(2)输出缓冲区大小设置:
设置项:client-output-buffer-limit
,具体设置内容:
- 设置缓冲区大小的上限阈值
- 设置输出缓冲区持续写入数据的数量上限阈值,和持续写入数据的时间阈值
与redis交互的客户端分为两种,与redis进行常规读写交互的普通客户端
以及订阅了redis频道的订阅客户端
,redis主从集群中主节点会有一类客户端(从节点客户端
)来与从节点交互
具体设置client-output-buffer-limit
:
// 0代表不做限制
client-output-buffer-limit normal[normal-普通客户端,pubsub订阅客户端,slave从节点客户端] 0[缓冲区大小限制] 0[持续写入量] 0[持续写入时长]
对于普通客户端一般都是阻塞式发送,因此一般无需对缓冲区进行限制。但是对于订阅客户端而言是非阻塞的,redis的频道一有消息就会通过缓冲区推送给客户端,因此是非阻塞的,需要做限制
client-output-buffer-limit pubsub 8mb 2mb 60
其中,pubsub参数表示当前是对订阅客户端进行设置;8mb表示输出缓冲区的大小上限为8MB,一旦实际占用的缓冲区大小要超过8MB,服务器端就会直接关闭客户端的连接;2mb和60表示,如果连续60秒内对输出缓冲区的写入量超过2MB的话,服务器端也会关闭客户端连接。
补充:
主库上的从库输出缓冲区(slave client-output-buffer,从节点客户端)是不计算在Redis使用的总内存中的,也就是说主从同步延迟,数据积压在主库上的从库输出缓冲区中,这个缓冲区内存占用变大,不会超过maxmemory导致淘汰数据。只有普通客户端和订阅客户端的输出缓冲区内存增长,超过maxmemory时,才会淘汰数据
(3)如何避免:
- 避免bigkey操作返回大量数据
- 避免在线上环境使用
MONITOR
命令 - 使用client-output-buffer-limit设置合理的缓冲区大小上限,或是缓冲区连续写入时间和写入量上限
3.主从缓冲区
主节点会为每一个从节点维护一个复制缓冲区,来保证数据不丢失
3.1.全量复制:
(1)溢出发生场景
- 全量复制时,主节点上的复制缓冲区本质上也是一个从节点客户端使用的输出缓冲区。复制缓冲区一旦发生溢出,也会导致连接中断,主节点会直接关闭和从节点进行复制操作的连接。
(2)如何避免
- 控制数据大小在2~4GB
- 设置参数:
client-output-buffer-limit
具体怎么设置。在主节点执行如下命令:
config set client-output-buffer-limit slave[复制缓冲区] 512mb 128mb 60
- 减少从节点的数量,如果从节点过多,会增加主节点的内存开销
3.2.增量复制
(1)发生场景
复制积压缓冲区(repl_backlog_buffer):增量复制时所使用的缓冲区,复制积压缓冲区是一个大小有限的环形缓冲区。当主节点把复制积压缓冲区写满后,会覆盖缓冲区中的旧命令数据,如果从节点还没有同步这些旧命令数据,就会造成主从节点间重新开始执行全量复制。
主节点在吧接收到的命令同步给从节点时,会将该命令放入复制积压缓冲区,一旦从节点宕机后,再次恢复时会从复制积压缓冲区,读取断联之后的命令,进而进行增量同步。
(2)解决措施
- 调整
repl_backlog_size
大小,详情见主从复制
4.总结
4.1缓冲区对Redis的影响
- 缓冲区溢出导致网络连接关闭:普通客户端、订阅客户端,以及从节点客户端,它们使用的缓冲区,本质上都是Redis客户端和服务器端之间,或是主从节点之间为了传输命令数据而维护的。这些缓冲区一旦发生溢出,处理机制都是直接把客户端和服务器端的连接,或是主从节点间的连接关闭。网络连接关闭造成的直接影响,就是业务程序无法读写Redis,或者是主从节点全量同步失败,需要重新执行。
- 缓冲区溢出导致命令数据丢失:主节点上的复制积压缓冲区属于环形缓冲区,一旦发生溢出,新写入的命令数据就会覆盖旧的命令数据,导致旧命令数据的丢失,进而导致主从节点重新进行全量复制
4.2.如何避免
从本质上看,缓冲区溢出,无非就是三个原因:命令数据发送过快过大;命令数据处理较慢;缓冲区空间过小。明白了这哥,我们就可以有针对性地拿出应对策略了。
- 针对命令数据发送过快过大的问题,对于普通客户端来说可以避免bigkey,而对于复制缓冲区来说,就是避免过大的RDB文件。
- 针对命令数据处理较慢的问题,解决方案就是减少Redis主线程上的阻塞操作,例如使用异步的删除操作。
- 针对缓冲区空间过小的问题,解决方案就是使用client-output-buffer-limit配置项设置合理的输出缓冲区、复制缓冲区和复制积压缓冲区大小。当然,我们不要忘了,输入缓冲区的大小默认是固定的,我们无法通过配置来修改它,除非直接去修改Redis源码。