问题描述:redis主从中断报错 Unable to partial resync with the slave for lack of backlog (Slave request was: 2595405802583).导致从机rdb每一分钟刷一次内存,
内存够用还有30G,这种积压导致每次主从全量同步,导致从机cpu总是报警,偶尔挂了
问题解决方法:
在主机上登录 设置以下参数
于是查看了一下client-output-buffer-limit。发现这是Redis的一个保护机制。配置格式是:
client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>
具体参数含义如下:
- class: 客户端种类,包括Normal,Slaves和Pub/Sub
- Normal: 普通的客户端。默认limit 是0,也就是不限制。
- Pub/Sub: 发布与订阅的客户端的。默认hard limit 32M,soft limit 8M/60s。
- Slaves: 从库的复制客户端。默认hard limit 256M,soft limit 64M/60s。
- hard limit: 缓冲区大小的硬性限制。
- soft limit: 缓冲去大小的软性限制。
- soft seconds: 缓冲区大小达到了(超过)soft limit值的持续时间。
client-output-buffer-limit参数限制分配的缓冲区的大小,防止内存无节制的分配,Redis将会做如下自我保护:
- client buffer的大小达到了soft limit并持续了soft seconds时间,将立即断开和客户端的连接
- client buffer的大小达到了hard limit,server也会立即断开和客户端的连接
再看看我们从库的这个配置,其实就是默认配置:
# 客户端的输出缓冲区的限制,因为某种原因客户端从服务器读取数据的速度不够快,
# 可用于强制断开连接(一个常见的原因是一个发布 / 订阅客户端消费消息的速度无法赶上生产它们的速度)。
# 可以三种不同客户端的方式进行设置:
# normal -> 正常客户端
# slave -> slave 和 MONITOR 客户端
# pubsub -> 至少订阅了一个 pubsub channel 或 pattern 的客户端
# 每个 client-output-buffer-limit 语法 :
# client-output-buffer-limit <class><hard limit> <soft limit> <soft seconds>
# 一旦达到硬限制客户端会立即断开,或者达到软限制并保持达成的指定秒数(连续)。
# 例如,如果硬限制为 32 兆字节和软限制为 16 兆字节 /10 秒,客户端将会立即断开
# 如果输出缓冲区的大小达到 32 兆字节,客户端达到 16 兆字节和连续超过了限制 10 秒,也将断开连接。
# 默认 normal 客户端不做限制,因为他们在一个请求后未要求时(以推的方式)不接收数据,
# 只有异步客户端可能会出现请求数据的速度比它可以读取的速度快的场景。
# 把硬限制和软限制都设置为 0 来禁用该特性
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
redis的replication buffer其实就是client buffer的一种。里面存放的数据是下面三个时间内所有的master数据更新操作:
- master执行rdb bgsave产生snapshot的时间
- master发送rdb到slave网络传输时间
- slave load rdb文件把数据恢复到内存的时间
可以看到跟replication backlog是一模一样的!
replication buffer由client-output-buffer-limit slave设置,当这个值太小会导致主从复制链接断开:
- 当master-slave复制连接断开,server端会释放连接相关的数据结构。replication buffer中的数据也就丢失了,此时主从之间重新开始复制过程。
- 还有个更严重的问题,主从复制连接断开,导致主从上出现rdb bgsave和rdb重传操作无限循环。
看起来确实server(这里就是master)会因为缓冲区的大小问题主动关闭客户端(slave)链接。因为我们的数据变更量太大,超过了client-output-buffer-limit。导致主从同步连接被断开,然后slave要求psync,但是由于repl-backlog-size太小,导致psync失败,需要full sync,而full sync需要Discarding previously cached master state,重新load RDB文件到内存,而这个加载数据过程是阻塞式的。所以导致slave出现间歇式的不可用。而切换到master之后,master的整个同步操作都是fork一个子进程进行的,所以不影响父进程继续服务。所有的现象都能清清楚楚的解释上。
从这个问题我们可以发现其实Redis的主从同步非常依赖于两个参数的合理配置:
- client-output-buffer-limit
- repl-backlog-size
真的要遇到问题,才能够深入的了解底层的实现机制啊。