记一次 Redis 主从同步问题导致的事故

Redis 自带主从功能,并且通过“哨兵”功能实现高可用。Redis 主从实现机制上与 Mysql 有点类似,但也有很大不同,Redis 同步机制详见>>。如果使用姿势不好则有可能导致重大问题。本文将记录一次实际事故来提醒 Redis 使用者以避免类似的问题。

架构说明

Redis 版本为 2.8。

开始时的 Redis 是简单的一主一从模式,业务层面进行读写分离。使用 Redis 自带的哨兵功能来监测服务可用性并当主出现问题时进行自动切主。后来由于业务访问量上来,导致使用的 Redis 的访问量也跟着上来了,而 Redis 占用了大量的带宽资源,出现了带宽不够用情况。后来就进行了扩容,从一主一从模式扩容为一主六从模式,这样就满足了带宽需求。

问题描述

依赖 Redis 的服务出现了不可用。跟踪分析发现访问 Redis 服务时出现了大量的错误(Redis 写已不可用,部分读不可用)。

总结主要有以下几个特征:

  • 主库的 CPU 满载;
  • Redis 日志显示有两个从节点在全量同步(Full Sync)数据,并且在不断重复;
  • Redis 日志显示这两个从节点丢失了(Lost);
  • 日志显示同步的缓冲区写满(omen=256M, qbuff-free=0)

问题分析

  • 由于机房内网波动,部分节点间主从同步出现中断,中断时间超过可以增量同步的容忍时间,从节点请求全量同步,同步过程中,网络依旧不稳定,导致出现(请求同步->闪断->请求同步)的恶性循环,主节点不断的全量 Dump Rdb 文件,buffer 写满,CPU 满载,从而集群稳定性降到极低,直到无法提供服务。
  • 哨兵切主过于频繁;

解决方案

  • 临时逐渐一台一台 kill 掉从节点,直到服务正常;
  • 增加哨兵的检查响应时间;
  • (优化) 减少从节点数量(1主3从),从而减少切主后过多的从库同时向主库拉取数据,占满主库带宽和 CPU;
  • 将数据库中无用的数据进行清理,达到从库能很快全量拉取玩主库数据;
  • 长期优化来看,应该将数据进行水平分割,不同的业务数据划分到不同的 Redis 集群中,使得单个 Redis 集群只做一主一从这样的简单架构,并且保证单个集群中数据量不要太大。

扩展

虽然 Redis 在 2.8 版本开始引入了 psync 增量同步模式,可以解决在短时间主从同步断掉重连的问题,但也有以下几个场景仍然会进行全量同步:

  1. 重启了主库/从库。因为 runnid 重启后就会丢失,所以当前机制只能进行全量同步;
  2. 当从库提升为主库。其他从库切到新主库全部要全量同步所有数据,因为新主库的 runnid 与老主库的不一样;
    在 Redis 4.0 对 psync 进行了优化,在一定程度上可以规避因为主从切换就要进行全量同步的问题。
  3. psync 命令携带的 offset 数值超过了设置的缓冲区大小则会进行全量同步;
  4. psync 命令携带的 runnid 值需要和主库记录的 runnid 一致才可以进行增量同步,否则全量同步;

在 4.0 前,做主从时,库数据量大情况下,架构上不能挂太多从,否则出现选新主后,新主忙于和各个从同步数据占用 cpu 和带宽,从而不能对外提供服务。