尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Redis主从心跳机制详解:PING、REPLCONF ACK与超时判定

Redis主从心跳机制详解:PING、REPLCONF ACK与超时判定 做Redis主从复制运维难免会遇到一类让人犯迷糊的现象主库负载正常从库也没任何报错但复制链路说断就断从库瞬间进入重连状态然后开始一整轮全量同步。很多人第一反应是“网络出问题了”可抓包一看TCP连接分明还活着。真正断掉的是应用层的心跳——主库发出的PING、从库回复的REPLCONF ACK在某个时间点没有按预期到达对方。这篇文章想把Redis主从心跳机制彻底讲透主库为什么默认10秒发一次PING、从库为什么要每秒回一个REPLCONF ACK并携带复制偏移量、repl-timeout到底是如何计算超时的以及作为后端开发或Redis运维怎么通过INFO replication、redis-cli --stat、tcpdump这些手段准确验证主从心跳是否健康。文章后面还会分享几个实际遇到过的故障案例和完整排查链路这些内容往往是常规文档里不会写的。无论你是刚入门Redis的开发者还是已经在生产环境维护主从集群的运维这篇内容都值得从头到尾过一遍。1. 主从心跳机制为什么存在长连接里的“静默”才是最大隐患1.1 主从复制的本质是一条长期存活的TCP连接Redis主从复制建立后主库和从库之间并不是按请求-响应的短连接方式工作而是一条常驻的TCP长连接。全量同步阶段主库把RDB快照通过这条连接推给从库全量完成后进入增量复制阶段主库把新的写命令持续通过这条连接发送给从库从库收到后执行保持数据一致。这条连接一旦建立在没有写入流量的时候会非常“安静”。比如一个读多写少的业务或者凌晨低峰期主库可能连续几十秒甚至几分钟没有任何新命令需要推送给从库。此时从库与主库之间的TCP连接上没有任何数据流动看起来一切正常实际上连接是否真的可用没有人知道。问题就出在这里如果中间某个交换机把这条闲置连接清了或者对端服务器网络栈出现异常TCP本身不会立刻察觉。连接不传数据双方都无法感知对面是否还活着。1.2 TCP keepalive为什么扛不住这个场景很多人会问Linux不是自带TCP keepalive吗没错但Redis没有完全依赖它原因是TCP keepalive默认太“迟钝”了。Linux默认的tcp_keepalive_time是7200秒也就是连接闲置2小时后才开始发送探测包。之后还要经过多次探测、每次间隔75秒才最终判定连接失效。这样一个故障从发生到被发现往往需要几十分钟甚至更久。对一个追求秒级可用性的分布式系统来说这种感知速度完全不合格。再深一层TCP keepalive只能探测到网络栈和连接层面的故障探测不到应用层的问题。如果从库的Redis进程主线程被某个超慢命令阻塞住比如执行了一个O(N)级别的大key操作线程卡死了TCP协议栈照样能正常响应keepalive但业务数据复制实际上已经停滞。这种“进程还活着、逻辑已经死了”的场景TCP无能为力必须靠应用层心跳来兜底。所以Redis在主从复制协议里设计了双层心跳主库周期性发送PING从库周期性回REPLCONF ACK。这套机制把故障发现时间压缩到了repl-timeout级别默认60秒比TCP自带的探测快了一个数量级。2. PING与REPLCONF ACK的完整工作链路周期、偏移量与超时判定2.1 主库的PING复制空闲期里的“敲门声”主库在正常同步命令流的过程中本身就有大量数据发往从库不需要额外“找话说”。但当复制进入空闲期主库超过一定时间没向从库写任何数据时就会主动发送一个PING命令。这个“一定时间”由配置项repl-ping-replica-period控制默认10秒。也就是说主库最迟每10秒会往从库连接上写一个PING。这样做有两个直接目的第一保持主到从方向的数据活跃。从库侧判断主库是否超时的依据是“多久没收到主库的数据了”。如果没有PING主库忙别的事情去了从库就会因为长时间收不到任何数据而误判主库下线。第二借助网络传输的过程提前暴露主到从方向的网络问题。如果主到从方向的链路已经半断开PING发不出去或者发出去后对端收不到双方都能通过超时机制更快发现。2.2 从库的REPLCONF ACK每秒汇报一次复制进度从库这边更主动。只要从库处于正常的已连接复制状态它会固定每1秒向主库发送一次REPLCONF ACK命令命令格式如下REPLCONF ACK slave_repl_offset其中slave_repl_offset是从库当前的复制偏移量。这个命令包含了两层信息一是“我还活着”二是“我当前的数据进度到哪里了”。主库收到这个ACK后会做两件事。第一刷新该从库的心跳记录用于延迟判定第二把自己当前的master_repl_offset和从库上报的offset做差得出从库的数据延迟量。这个差值越大说明从库落后主库越多。所以你仔细看会发现标题里说的“从库回复REPLCONF ACK”这个表述非常精确——它本质上是从库主动上报而不是对主库PING的一问一答。主库的PING和从库的ACK是两条独立的心跳信号各自维护一个方向上的活跃度合起来才构成完整的主从健康检查。2.3 repl-timeout超时判定双向的“一票否决”repl-timeout是这套机制里最终的裁决者默认值是60秒。它的作用不是限制某条命令的执行时间而是规定在指定的超时时间内一方如果没有收到另一方任何有效数据就判定对方已不可用。主库侧的逻辑是如果在repl-timeout时间内没有收到某个从库的REPLCONF ACK主库就会断开这个从库的连接。断开后从库会发现连接掉了于是重新发起握手重新走全量或增量同步流程。从库侧的逻辑是对称的如果在repl-timeout时间内没有收到主库发送的任何数据包括命令流和PING从库会判定主库已经失联主动断开连接并尝试重新连接主库。这里有个容易踩坑的点repl-timeout是一个综合超时它不区分连接是否建立过多少次。它针对的是“空闲超时”即长时间没有数据交互。如果主从之间持续有数据流动比如高写入场景下每秒成千上万的命令在同步那么即使超过60秒只要数据还在传这个超时就不会触发。真正触发超时的场景几乎总是出现在数据空闲期或者链路异常期。三个核心参数的对比关系如下参数默认值发送方作用repl-ping-replica-period10秒主库复制空闲时保证主到从方向有活跃数据REPLCONF ACK1秒/次从库上报从库存活状态与复制偏移量repl-timeout60秒双方生效超时未收到任何数据则判定对方失联设计上的黄金法则是repl-timeout必须显著大于repl-ping-replica-period同时大于从库ACK的间隔。默认配置下主库即使错过5次PING窗口或60次ACK窗口才会触发断开容错空间相当充足。这也是我建议不要轻易把repl-timeout调小到30秒以下的原因后面排查案例会专门讲。3. 动手验证第一步用INFO replication读出主从心跳的健康状态3.1 主库视角connected_slaves与slave0行里的lag字段验证主从心跳健康最直接的办法是登录主库执行redis-cli info replication正常状态的主库输出大致如下# Replication role:master connected_slaves:1 slave0:ip192.168.1.102,port6379,stateonline,offset1234567,lag0 master_failover_state:no-failover master_repl_offset:1234567 repl_backlog_active:1 repl_backlog_size:1048576重点关注slave0这一行里的state和lag字段。state为online表示主库认为该从库在线lag为0或1表示刚刚收到了该从库的ACK这是健康状态。lag的单位是秒含义是“距离上一次收到这个从库REPLCONF ACK过去了多长时间”。你可以把这个lag理解成从库的心跳“脚印”。从库每1秒踩一个脚印主库检查最近一次脚印离现在多久了。如果lag稳定在0或1说明从库心跳完全正常如果lag开始变成3、5、10说明从库的ACK已经不能按时到达了此时虽然连接还没断但已经处于亚健康状态。当lag持续增长并最终超过repl-timeout默认60秒主库会直接断开连接。断开后你会在INFO replication里看到这个从库从slave0行消失connected_slaves的数量也会相应减少。3.2 从库视角master_link_status与master_last_io_seconds_ago反过来登录从库执行同样的命令redis-cli info replication从库视角的关键字段是master_link_status和master_last_io_seconds_ago# Replication role:slave master_host:192.168.1.101 master_port:6379 master_link_status:up master_last_io_seconds_ago:0 master_sync_in_progress:0 slave_repl_offset:1234567 slave_priority:100 slave_read_only:1 connected_slaves:0 master_repl_offset:0master_link_status为up代表从库认为主库在线down则代表已经判定主库失联。master_last_io_seconds_ago是距离上一次与主库进行IO交互的秒数正常情况下应该非常小。如果这个值持续变大说明主库方向的数据流包括PING和命令流已经中断了一段时间。这里有个经验值供参考master_last_io_seconds_ago一旦超过repl-timeout的一半就要引起警惕超过8成的时候基本可以断定主从之间的网络或主库本身已经出了问题。3.3 三种典型状态的INFO输出对比为了更直观地理解怎么判断我列一个三种状态的对比状态主库看到的lag从库看到的master_link_status含义健康0或1uplast_io为0-1秒心跳正常复制零延迟或极低延迟亚健康持续上升如5-30up但last_io也在上升链路有丢包/从库阻塞ACK无法按时到达已断线从列表消失down超时判定连接被断开进入重连流程实际工作中我建议把lag检查做成一个定时监控脚本每10秒或者每30秒扫一次redis-cli -h 127.0.0.1 -p 6379 info replication | grep -E slave[0-9]:.*lag连续多次扫到lag大于5就该告警出来看看了。真等到lag超过60秒往往已经在全量同步的路上了业务影响已经造成。4. 更细微的观察用stat、monitor和tcpdump把PING与ACK抓到眼前4.1 redis-cli --stat适合快速瞄一眼如果你想在本地快速观察主从复制期间的流量特征可以用自带的实时统计命令redis-cli --stat它会以每秒一次的频率刷新ops、内存、客户端连接数、网络吞吐等指标。不过说实话--stat主要是看整体负载对于心跳这种低频小包它的粒度不够细。它能告诉你主从之间有没有网络活动但说不清是业务命令还是心跳包。所以我通常只用它做第一印象判断真正的细节还得靠下面两种手段。4.2 monitor命令直接看到REPLCONF ACK命令但有代价Redis的monitor命令可以实时打印服务器收到的所有命令在主库上执行时你能直接看到从库每秒发来的REPLCONF ACK1667812345.123456 [0 192.168.1.102:6379] REPLCONF ACK 1234567 1667812346.123456 [0 192.168.1.102:6379] REPLCONF ACK 1234567 1667812347.123456 [0 192.168.1.102:6379] REPLCONF ACK 1234568注意看每条记录后面跟着的offset在持续增长这说明从库不仅活着而且一直在消费主库的命令流。如果在monitor里长时间看不到某个从库的REPLCONF ACK那就说明它没有发上来问题大概率出在从库线程阻塞或网络链路。必须提醒一下monitor在生产环境对性能影响很大它会降低Redis吞吐量高负载实例上开着monitor等于给自己埋雷。建议只在低峰期短时间使用或者临时在测试环境观察。4.3 tcpdump抓包还原PING与ACK的真实流量时序想看到最底层的事实最可靠的手段是抓包。在主库侧抓6379端口tcpdump -i eth0 -nn -XX -s 0 port 6379在主从空闲的场景下你会看到非常规律的两类包一类是主库发出的PING命令包间隔大约10秒192.168.1.101.6379 192.168.1.102.xxxxx: P 1:34(33) ack 123 *1\r\n$4\r\nPING\r\n另一类是从库每秒发来的REPLCONF ACK包192.168.1.102.xxxxx 192.168.1.101.6379: P 456:512(56) ack 234 *3\r\n$8\r\nREPLCONF\r\n$4\r\nACK\r\n$7\r\n1234567\r\n抓包最大的价值在于能直接看到数据是否真的到达了网卡。很多时候INFO显示lag在涨但看起来连接还没断抓包一抓发现TCP层其实已经出现大量重传retransmission了这说明网络链路丢包严重。还有一种更隐蔽的情况如果发现主库根本没发出PING说明问题不在网络而在主库自身的复制线程或事件循环上。判断TCP重传可以直接用tcpdump的-v参数重传包会标记为“retransmission”tcpdump -i eth0 -nn -v port 6379 | grep -i retransmission如果重传数量在短时间内激增基本可以锁定网络层问题。5. 心跳异常的典型场景与排查链路从lag飙升到主库误判从库5.1 场景一网络层丢包导致ACK迟到主库误判后触发全量同步这是我在生产环境遇到最多的情况。表象是主库没有明显高负载从库也没有慢查询但从库隔三差五就进入重连状态日志里频繁出现全量同步记录。排查时先看主库的INFO replication发现某个从库在断线前lag一路飙升0、1、3、8、20、45然后就消失了。这个曲线非常典型说明从库的ACK根本没到主库而不是从库没发——因为从库侧日志没有任何异常。接着抓包发现TCP层有大量重传特别是从从库发往主库的方向。进一步检查网络设备才发现两台机器所在的交换机端口之间存在间歇性丢包间歇期正好和从库断线时间吻合。这个案例里有个关键教训从库的ACK每秒只有一次而且数据量极小网络只要在某个时刻丢几个包主库这边就可能连续几十秒收不到ACK直接触发超时断开。相比业务大流量这种小包对丢包更敏感。5.2 场景二从库被慢查询拖住线程阻塞发不出ACK另一种让lag飙升的原因在从库自身。比如有人在从库上执行了一个keys *匹配大量key的扫描或者对一个包含几百万元素的大list做了类似LRANGE 0 -1的操作Redis是单线程模型从库主线程一旦被这类命令卡住复制命令流和每秒的ACK发送全部都会被阻塞。这时候主库侧的症状也是lag持续升高最终断开连接。区别在于从库侧你能看到当前正在执行的命令是什么。解决方案是给Redis设置慢查询日志阈值然后持续监控redis-cli config set slowlog-log-slower-than 10000 redis-cli slowlog get 5生产环境务必提前阻止危险命令至少要把keys、flushall等命令通过rename-command配置改名禁用避免人为触发从库阻塞。5.3 场景三repl-timeout配置过小心跳稍慢就被“判死刑”有些人觉得repl-timeout默认60秒太久想缩短故障感知时间把repl-timeout调到了20秒甚至10秒。这个初衷可以理解但风险很大。默认配置下主库要连续6秒收不到ACK才会断开因为ACK每秒一次。如果你把repl-timeout调到20秒那么网络只要抖动超过20秒主库就会断开一个其实还活着的从库。而20秒的网络抖动在跨机房链路上并不罕见。断开之后从库重连如果增量复制条件不满足就会触发全量同步几千个G的RDB文件一来网络和磁盘的压力远大于你通过缩短超时时间省下的那点故障感知时间。我的建议是repl-timeout保持默认60秒不要动。如果确实需要更快感知优先优化网络监控体系而不是压缩Redis的心跳容忍度。5.4 从现象到结论一条可复现的完整排查链路把前面几个场景综合起来当遇到主从心跳异常时我建议按下面这个顺序排查查主库跑info replication看哪些从库lag异常或哪些从库已断开。查从库跑info replication看master_link_status和master_last_io_seconds_ago判断是哪个方向出了问题。看慢日志进从库查slowlog get 20确认是否有慢命令阻塞主线程。抓包确认在主从两台机器上分别tcpdump抓6379端口确认PING和ACK是否到了对端网卡重点关注TCP重传。验证网络链路用ping命令配合RTT统计初步判断延迟和丢包。要注意ICMP的ping通不代表6379端口的TCP链路就正常更准确的做法是直接在两台机器之间用redis-cli互相执行PING命令验证应用层连通性。确认配置检查repl-timeout、repl-ping-replica-period是否被修改过以及两边的配置是否一致。按这个链路走一遍绝大多数心跳问题都能定位到具体环节。很多时候你会发现Redis本身没毛病是周边环境在“干扰”它的心跳。5.5 min-replicas-to-write心跳健康影响业务的典型例子最后补一个容易忽视的知识点心跳健康度不仅影响复制链路本身还会直接影响主库的写入可用性。如果你配置了min-replicas-to-write和min-replicas-max-lag比如min-replicas-to-write 1 min-replicas-max-lag 10那么主库会要求至少有一个从库的lag在10秒以内才允许处理写命令。当所有从库的lag都超过10秒时主库会直接拒绝写入业务侧会看到类似“No good replicas”的错误。这个机制的本质是把心跳健康检查和数据安全策略联动起来。它的目的是防止主库在没有任何可用从库的情况下继续写入导致一旦主库宕机连一个能接管的从库都没有。但也因为这样主从心跳的健康就不再只是运维指标而是直接影响业务连续性的关键因子。从库lag监控的告警阈值最好要小于min-replicas-max-lag这样才能在写保护触发之前提前介入处理。我在实际维护集群的时候会把lag的告警阈值定在5秒min-replicas-max-lag定在10秒repl-timeout保持60秒。这样三层阈值拉开梯度从发现异常到最终写保护中间有足够的处理时间。这套配置下来主从心跳的健康情况基本能做到可视、可控、可干预而不是每次都在全量同步风暴发生后才追悔莫及。
返回列表