记一次redis主从切换导致的数据丢失与陷入只读状态故障

在进行Redis内存扩容过程中,主从切换导致数据丢失并使master进入只读状态。问题源于主从切换后新master的maxmemory配置错误。故障处理后发现,未同步配置文件的maxmemory值,以及replica-ignore-maxmemory参数导致数据丢失。解决方案是确保运行时和静态配置的一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

最近一组业务redis数据不断增长需要扩容内存,而扩容内存则需要重启云主机,在按计划扩容升级执行主从切换时意外发生了数据丢失与master进入只读状态的故障,这里记录分享一下。

业务redis高可用架构

该组业务redis使用的是一主一从,通过sentinel集群实现故障时的自动主从切换,这套架构已经平稳运行数年,经历住了多次实战的考验。
高可用架构大体如下图所示:
在这里插入图片描述

简单说一下sentinel实现高可用的原理:
集群的多个(2n+1,N>1)哨兵会定期轮询redis的所有master/slave节点,如果sentinel集群中超过一半的哨兵判定redis某个节点已主观下线,就会将其判定为客观下线进行相应处理:

  1. 如果下线节点是master,选定一个正常work的slave将其选定为新的master节点。
  2. 如果下线节点是slave,将其从slave节点中移除。

如果已经被客观下线的节点恢复了正常,sentinel中超过一半哨兵确认后则将其加回可用的slave节点。
所有需要读写redis的server并不需要直接写死redis 主从配置,而是通过访问sentinel获取当前redis的主从可用状态,具体实现方式可以定时查询sentinel询问更新,也可以通过订阅机制让sentinel在主从变动时主动通知订阅方更新。
sentinel实现高可用的详细原理这里不做过多赘述,有兴趣的小伙伴可以移步参考文献中的相关资料。

具体内存扩容流程

sentinel可以在检测到故障时自动切换redis主从,也可以主动执行sentinel failover mastername 命令实现手动切换主从,所以这次的内存扩容重启流程设计如下(A代表初始master所在云主机,B代表初始slave所在云主机):

  1. 升级主机B内存配置,重启主机B
  2. 检查B重启后其上的redis slave是否重新同步master数据完成,包括:
    2.1 查看slave redis log是否异常,无异常pass2.2 使用info keyspace命令check master、slave 各db key数量是否一致,无异常pass2.3 在master写入一个测试key,在slave上check是否同步成功2.4 观察依赖server log是否有异常
  3. 使用sentinel failover mastername命令手动主从切换,主机A变成新slave,主机B变成新master,根据以前手动切换的经验走到这一步基本上就稳了–因为这里本质上和一次普通主从切换已经没有区别了。
    升级主机A内存配置,重启主机A,执行以下check:
  4. 4.1 查看新slave redis log是否异常4.2 使用info keyspace命令check 新master、新slave 各db key数量是否一致,无异常pass4.3 在新master写入测试key,在新slave上check是否同步成功4.4 观察依赖server log是否有异常
    至此,若以上步骤都正常通过,一个完美的redis内存升级工作就完成了。

主从切换后数据丢失

结果正是没有想过可能会出问题的步骤3反而出现了问题,直接导致了主从切换后丢掉了部分数据,并且新master进入只读状态将近十分钟。

当时的情况是这样的:

在执行完步骤3后,check 新slave redis log无异常,正在考虑观察一会儿后执行主机A的升级重启操作,api的分钟级别异常监控触发了一小波redis相关报警。第一反应在新master与新slave上执行了info keyspace查看key数量是否已经不一致,结果发现master/slave的key数量是一致的–但是再仔细一看:和切换前的key总数百万级相比切换后key总数降到了十万级–大部分key数据被丢失了。
查看新master、新slave log都没有发现明显log可以解释为什么主从切换后会丢失一大半数据这一现象,这时小伙伴第一次提到了是不是内存不够了,当时自己略一思考马上回复到:新master刚升级了内存,不可能内容扩大后反而内存不足的,所以应该不是这个问题。

n分钟后…

小伙伴再一次提出了是不是maxmemory问题,这一下子点中了关键点,马上想到主机B升级了内存是不会有系统层面内存不足的问题,但是redis的内存使用实际上还会受到maxmemory参数限制,马上在新master上执行config get maxmemory, 只有3GB,而升级前数据实际使用内存超过了6GB!
立刻调大了新master的maxmemory参数,redis很快恢复了可读写正常状态,一大波redis只读引发的告警通知开始快速下降。

原因定位

紧张又刺激的故障处理就这么过去了,在优先处理完丢失key数据恢复工作之后,开始回顾整理故障的详细原因,总共有如下几个疑问:

  1. 明确记得上个月给主机A、B上的redis都通过config set maxmemory设置为了7GB,为什么出现问题时查询B上redis 的maxmemory配置却变成了3GB?
  2. 如果主机B的maxmemory是3GB,其作为slave时为什么从master同步超过6GB的数据时不会有问题?–在主从切换前无论是查看info keyspace还是在master上写入测试key同步check都是OK的。
  3. 为什么主从切换后主机B上的key数据会丢失?这个是因为maxmemory设置过小,是故障的直接原因。
  4. 为什么新master由于maxmemory参数超限进入只读状态且删除部分数据后,新master中实际数据占用的大小依然超过>3GB?

如上四个疑问除了问题3已经明确了,剩下三个问题都让人疑惑–事出诡异必有妖,经过一番探寻得出其答案:

  1. 上个月修改redis maxmemory时,只通过config set命令修改了其运行时配置,而没有修改对应配置redis.conf上maxmemory的值,主机B上redis在重启后就会从redis.conf上载入该maxmemory,该配置正是3GB,同时maxmemory参数是redis节点独立的配置,slave并不会从master同步该值。
  2. 在redis5.0版本之后,redis引入了一个新的参数replica-ignore-maxmemory,其官方文档定义如下:
Maxmemory on replicas
By default, a replica will ignore maxmemory (unless it is promoted to master after a failover or manually). It means that the eviction of keys will be handled by the master, sending the DEL commands to the replica as keys evict in the master side.
This behavior ensures that masters and replicas stay consistent, which is usually what you want. However, if your replica is writable, or you want the replica to have a different memory setting, and you are sure all the writes performed to the replica are idempotent, then you may change this default (but be sure to understand what you are doing).
Note that since the replica by default does not evict, it may end up using more memory than what is set via maxmemory (since there are certain buffers that may be larger on the replica, or data structures may sometimes take more memory and so forth). Make sure you monitor your replicas, and make sure they have enough memory to never hit a real out-of-memory condition before the master hits the configured maxmemory setting.
To change this behavior, you can allow a replica to not ignore the maxmemory. The configuration directives to use is:
replica-ignore-maxmemory no

大意是redis作为slave时默认会无视maxmemory参数,这样可以保证主从的数据始终保持一致。当master/slave实际数据大小均小于其maxmemory设置时,这个参数没有任何影响,而这次丢失数据的原因正是因为主机B重启后作为slave时maxmemory(3GB)小于实际数据大小(6GB+),此时replica-ignore-maxmemory 默认开启保证作为slave时直接无视maxmemory的限制,而当执行sentinel failover mastername将主机B切换为新master后,新master不会受replica-ignore-maxmemory影响,发现自身maxmemory<实际数据大小后直接开始主动淘汰key,从而导致了数据丢失。
3. 至于主机B作为master执行淘汰key策略并最终进入只读状态后,其实际数据大小依然>3GB的原因,则是由于线上redis配置的策略是volatile-lru策略,该策略只会淘汰有过期时间的key,对于不过期的key是不淘汰的。

总结

总的来看这次故障的根本原因还是个人对于redis的配置、操作经验不足,如果在调整运行时maxmemory时能做到以下二者之一,这次故障就不会出现了:

  1. 调整运行时maxmemory时同时调整配置文件maxmemory保持一致。
  2. 将配置文件maxmemory设置为0–表示不限制内存使用。

正是因为对redis的认识和经验不足,没有想过到运行时配置与静态配置不一致可能导致的问题,这次不可避免的踩坑了。
但是,作为一个本职RD,半路接手基本靠自学的兼职运维,要考虑到maxmemory的运行配置与静态配置一致性问题好像也确实不是那么的理所当然。
处理完这次故障后,特意在网上搜索了一番redis主从切换的注意事项、踩坑文章,想看看有没有人提到类似的坑,但是并无所获,难道这个坑真的没其他人踩(分享)过?陷入思考…

最后

如果有经验丰富的小伙伴看到这里,也欢迎不吝赐教指导一下redis主从的切换的各类常识与常见大坑!

<think>好的,我现在需要帮助用户解决关于Redis主从配置的问题。用户提到了最佳实践和教程,所以我需要详细说明配置步骤,并结合引用内容来支持回答。首先,我应该回顾一下用户提供的引用资料,看看哪些可以用于回答。 引用[3]提到了从节点的配置文件,里面包括replicaof指令,这应该是配置主从的关键部分。引用[5]提到三主三从架构,所有节点互联,使用PING-PONG机制,这可能涉及主从同步和集群配置的最佳实践。另外,引用[2]提到了监控工具,这可能和配置后的监控有关,确保主从正常运行。 接下来,我需要整理主从配置的步骤。首先,安装Redis,然后修改主节点和从节点的配置文件。主节点可能需要设置bind和port,从节点则需要使用replicaof命令指向主节点。然后启动实例,验证同步情况,最后考虑持久化和监控。 需要注意主节点的写入和从节点的只读,以及如何添加多个从节点。另外,最佳实践部分应该包括高可用架构,比如三主三从,以及使用监控工具。可能还需要提到故障处理,比如主节点宕机后的切换,但用户的问题主要是配置,这部分可以简略。 需要确保步骤清晰,每一步都有对应的引用支持,比如配置文件示例来自引用[3],架构建议来自引用[5]。同时,生成的相关问题要围绕主从配置的扩展内容,比如同步原理、故障处理、监控优化等。</think>### Redis主从配置方法最佳实践 #### **一、主从配置步骤** 1. **安装Redis** 确保所有节点安装相同版本的Redis,避免版本差异导致兼容性问题。使用包管理工具(如`apt`或`yum`)或源码编译安装。 2. **主节点配置** 编辑主节点的配置文件 `redis-master.conf`,关键参数如下: ```conf bind 0.0.0.0 # 允许所有IP访问 port 6379 # 主节点端口 daemonize yes # 后台运行 requirepass 123456 # 可选:主节点密码(从节点需同步配置) ``` 启动主节点: ```bash redis-server /path/to/redis-master.conf ``` 3. **从节点配置** 编辑从节点配置文件 `redis-slave.conf`,通过`replicaof`指令关联主节点[^3]: ```conf bind 0.0.0.0 port 6380 daemonize yes replicaof 192.168.1.100 6379 # 主节点IP和端口 replica-read-only yes # 从节点只读 masterauth 123456 # 若主节点有密码,需配置此项 ``` 启动从节点: ```bash redis-server /path/to/redis-slave.conf ``` 4. **验证主从同步** 在主节点写入数据,从节点查询是否同步: ```bash # 主节点写入 redis-cli -p 6379 set key1 "hello" # 从节点读取 redis-cli -p 6380 get key1 ``` 若返回`"hello"`,则同步成功。 5. **配置持久化(可选)** 建议主从节点均开启RDB或AOF持久化,防止数据丢失: ```conf save 900 1 # RDB触发条件 appendonly yes # 开启AOF ``` --- #### **二、最佳实践** 1. **高可用架构** - 采用**三主三从**架构,主节点提供服务,从节点用于故障切换数据备份[^5]。 - 所有节点通过`PING-PONG`机制保持通信,确保网络健康。 2. **读写分离** - 主节点处理写请求,从节点处理读请求,通过客户端或代理(如Redis Sentinel)实现负载均衡。 3. **监控优化** - 使用`INFO replication`命令查看主从状态,或通过RedisInsight等工具监控性能[^2]。 - 定期检查同步延迟(`repl_offset`),避免数据不一致。 4. **故障处理** - 主节点宕机时,可通过`SLAVEOF NO ONE`提升从节点为主节点,再重构其他从节点关系。 - 使用**Redis Sentinel**实现自动故障转移(引用哨兵配置[^4])。 --- #### **三、配置文件示例** - **主节点**:`redis-master.conf` ```conf bind 0.0.0.0 port 6379 daemonize yes ``` - **从节点**:`redis-slave.conf` ```conf bind 0.0.0.0 port 6380 daemonize yes replicaof redis-master 6379 replica-read-only yes ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值