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

资讯详情

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

Redis主从复制实战:从原理到生产环境搭建与故障排查

Redis主从复制实战:从原理到生产环境搭建与故障排查 1. 先回答一个问题一台 Redis 为什么扛不住生产环境单机 Redis 在开发环境和本地 Demo 里很好用启动快、命令简单、数据也能落盘。但一旦部署到生产环境一台 Redis 实例很快会暴露三个层面的问题。第一是可用性。Redis 默认运行在 6379 端口进程一旦异常退出、服务器宕机或者内存碎片导致不可用所有直接依赖 Redis 的请求都会立刻失败。最常见的是高并发请求全部打到数据库数据库连接数被打满整个链路雪崩。有人会说可以重启但进程重启少则几秒多则几十秒这个时间窗口足够让业务层产生大量异常。第二是读性能。Redis 本身是单线程事件循环模型单个实例的 OPS每秒操作数通常在十万级具体取决于命令复杂度、网络时延和硬件资源。真实业务里读请求往往远多于写请求比如商品缓存、配置中心、用户会话读占比可能高达 80% 以上。此时单实例的 CPU 会成为瓶颈而且无法通过加内存解决因为瓶颈在单线程处理能力。第三是数据安全。虽然有 RDB 和 AOF 两种持久化机制但持久化文件只存在于本机。磁盘损坏、误删除、机房故障都会导致数据无法恢复。RDB 快照触发时 fork 子进程做内存快照在大实例上可能短暂阻塞主进程AOF 重写也有类似消耗。如果只有一台机器这些备份操作和高可用策略几乎没有回旋余地。主从复制Replication就是为了解决这些问题出现的基础机制。它做的事情很直接一台节点作为主节点Master负责处理写请求一个或多个从节点Replica实时同步主节点的数据从节点对外提供读服务。写入集中在主节点读压力分散到从节点主节点故障时由从节点接管数据持久化备份也可以放到从节点执行。这篇文章的核心主线是理解 Redis 主从复制解决什么问题然后手动搭建一个主从环境观察一次完整的数据同步过程最后掌握常见的同步故障排查方法。不建议直接跳到哨兵或集群主从复制是这两者的基础哨兵负责的是自动故障转移集群解决的是数据分片它们都建立在数据能被复制这一前提之上。2. 主从复制到底复制了什么又解决了什么问题2.1 复制的是“写操作的结果”不是存储文件严格说Redis 主从复制并不是把 RDB 文件或者 AOF 文件定时拷贝到从节点而是通过一组协议保持主从数据一致。从节点初次连接主节点时主节点会生成一份当前数据的快照RDB发给从节点之后主节点再将复制期间产生的新写命令持续发给从节点。这样从节点在绝大多数时间内和主节点保存着相同的数据状态。这种设计的好处是复制过程对业务写入的影响很小。主节点只需要 fork 出一个子进程生成 RDB 文件然后持续发送后续命令从节点加载快照后继续执行增量命令最终达到一致状态。后续的网络断开重连也优先使用少量命令做增量同步而不是每次都把全量数据重发一遍。2.2 主从复制带来的三个直接收益从工程角度看主从复制至少带来三点变化读写分离。主节点处理写请求从节点处理读请求。读多写少的业务可以把绝大多数读流量分散到多个从节点降低主节点压力。数据热备。即使主节点发生故障数据已经同步到从节点可以手动把从节点切换为主节点或由哨兵自动完成切换缩短业务中断时间。备份与维护窗口隔离。在从节点上执行持久化、生成备份、执行大数据分析都不会影响主节点的写入性能。2.3 必须明白的一点主从复制不等于高可用很多初学 Redis 的人有个误区以为做了主从复制Redis 就具备故障自愈能力了。实际上主从复制只解决数据同步问题不解决自动选主问题。如果主节点挂掉从节点虽然有全部数据但不会自己成为主节点也不会自动接收写请求。默认情况下从节点是只读的业务写入会直接报错。要真正实现故障自动转移必须在主从复制之上再部署哨兵Sentinel或者使用 Redis Cluster 自带的高可用能力。这个定位关系很重要后面章节会继续展开。3. 环境准备用 Docker Compose 快速搭一个一主两从主从复制可以在单机上通过多个 redis-server 实例模拟也可以用 Docker Compose 快速编排。这里推荐 Docker Compose隔离性好配置清晰适合作为学习环境。3.1 环境要求项目要求说明Docker20.10 以上兼容新版 Compose v2 插件Docker Composev2 或 v1 均可v2 推荐直接使用docker compose命令Redis 镜像redis:7.0 或 redis:7.2两个镜像配置语法基本一致宿主机端口6379、6380、6381确保没有冲突或用容器内网络测试如果还没有安装 Docker可以先安装 Docker Desktop 或 Linux 发行版对应 docker 包。整个搭建过程不需要独立编译 Redis节省很多时间。3.2 目录结构redis-replication-lab/ ├── docker-compose.yml ├── master/ │ └── redis.conf ├── replica1/ │ └── redis.conf └── replica2/ └── redis.conf这个结构便于分别挂载不同节点的配置和数据目录。实际生产中往往还要挂载日志目录学习环境可以先不配。3.3 主节点配置创建master/redis.confport 6379 bind 0.0.0.0 protected-mode no appendonly yes appendfilename appendonly.aof dir /data主节点只需要保证允许从节点连接即可。protected-mode no是为了让容器外的从节点可以访问本机学习环境可以这样用生产环境必须配置密码和防火墙策略不能直接暴露。3.4 从节点配置创建replica1/redis.confport 6379 bind 0.0.0.0 protected-mode no replicaof redis-master 6379 replica-read-only yes appendonly yes appendfilename appendonly.aof dir /data再创建replica2/redis.conf内容和 replica1 相同。replicaof redis-master 6379指定了主节点的主机名和端口。Docker Compose 中容器间通常用服务名互相访问这里的服务名就是redis-master。3.5 Docker Compose 编排文件创建docker-compose.ymlversion: 3.8 services: redis-master: image: redis:7.0 container_name: redis-master command: [redis-server, /usr/local/etc/redis/redis.conf] ports: - 6379:6379 volumes: - ./master/redis.conf:/usr/local/etc/redis/redis.conf - ./master/data:/data redis-replica1: image: redis:7.0 container_name: redis-replica1 depends_on: - redis-master command: [redis-server, /usr/local/etc/redis/redis.conf] ports: - 6380:6379 volumes: - ./replica1/redis.conf:/usr/local/etc/redis/redis.conf - ./replica1/data:/data redis-replica2: image: redis:7.0 container_name: redis-replica2 depends_on: - redis-master command: [redis-server, /usr/local/etc/redis/redis.conf] ports: - 6381:6379 volumes: - ./replica2/redis.conf:/usr/local/etc/redis/redis.conf - ./replica2/data:/datacommand里的路径必须和容器内挂载路径完全一致否则 redis-server 找不到配置。宿主机端口和容器端口要区分开容器内统一用 6379宿主机分别映射到 6379、6380、6381。3.6 启动并检查复制状态在redis-replication-lab目录下执行docker compose up -d docker compose ps等服务全部启动后进入主节点容器查看复制信息docker exec -it redis-master redis-cli info replication预期输出类似role:master connected_slaves:2 slave0:ip172.18.0.3,port6379,stateonline,offset28,lag0 slave1:ip172.18.0.4,port6379,stateonline,offset28,lag0 master_replid:e0e5c0e7f8a3a6a7e52c975e1c46b536e0b0c8ba master_replid2:0000000000000000000000000000000000000000 master_repl_offset:28出现两个stateonline的从节点表示复制链路已经建立。如果state不是online而是connect或sync说明网络不通或配置没生效。4. 主从复制的核心机制全量同步、增量同步和心跳搭建成功后接下来研究底层机制。理解这些机制对排查同步延迟、断线重连和数据丢失问题非常关键。4.1 replid 和 offset判断传输进度的两个核心变量每个 Redis 主节点拥有两个标识复制状态的变量master_replid主节点实例的唯一复制 ID可以理解为当前“数据历史”的版本标识。每次实例重新启动或者执行REPLICAOF NO ONE变成主节点后都会生成新的 replid。master_repl_offset已处理的写命令字节数偏移量。从节点每执行完一条同步命令也会向主节点汇报自己的偏移量。主节点通过对比master_replid和偏移量来判断从节点的数据落后程度。如果从节点保存的主节点 replid 和当前主节点相同且偏移量小于主节点偏移量则只需要发送中间缺失的命令段。如果 replid 不同说明从节点曾经连接过另一个主节点或者主节点重启过必须做一次全量同步。4.2 全量同步流程第一次连接或 replid 不匹配时会发生全量同步流程可以概括为从节点发送PSYNC ? -1表示不知道主节点的 replid 和偏移量请求全量复制。主节点检查 replid 后返回FULLRESYNC replid offset触发子进程生成 RDB 快照。主节点把 RDB 文件通过 socket 发送给从节点同时把生成快照期间产生的新写命令缓存到复制积压缓冲区repl_backlog_buffer。从节点清空旧数据并加载 RDB加载完成后继续接收主节点发送的增量命令。RDB 文件大小直接决定全量同步耗时。几 GB 的实例做全量同步会占用大量带宽和磁盘 IO从节点加载期间可能短暂不可用。生产环境要尽量避免频繁全量同步。4.3 增量同步与 repl_backlog 缓冲区网络断开重连时如果主节点 replid 没变且从节点的偏移量仍在主节点保存的 repl_backlog 范围内就可以做增量同步。repl_backlog 是一个环形缓冲区默认大小是 1MB通过配置参数repl-backlog-size调整。同步流程是从节点重连后发送PSYNC replid offset主节点检查偏移量是否落在 repl_backlog 中。如果命中返回CONTINUE并发送 offset 之后缺失的命令如果没有命中则直接触发全量同步。这个设计解决了一个关键问题断线时间短、写命令少时不需要重新传输整个数据集只需要把断线期间的命令补上。实际应用中如果断线时间很长超过 repl_backlog 的覆盖范围就会退化为全量同步这也是很多大实例同步风暴的主要原因。4.4 主从之间的心跳检查从节点默认每 10 秒向主节点发送一次REPLCONF ACK offset主节点通过info replication中的lag字段观察最近一次通信间隔。如果 lag 值持续变大说明主从之间的网络延迟或丢包比较严重。主节点也会定期向从节点发送 PING默认周期为 10 秒通过repl-ping-replica-period配置。生产环境不建议把心跳频率调得过高否则大量从节点会带来额外的网络包也不建议过低否则故障检测会变慢。可以做一个简单实验来验证心跳在主节点上写入数据然后观察从节点是否能立刻读到docker exec -it redis-master redis-cli set mykey hello docker exec -it redis-replica1 redis-cli get mykey docker exec -it redis-replica2 redis-cli get mykey正常情况三次都会输出hello。如果从节点读取为空说明复制链路中断或从节点还在同步中。5. 从节点只读、读写分离和一致性边界5.1 从节点默认只读写操作会被拒绝Redis 从节点默认配置replica-read-only yes直接向从节点写入会得到(error) READONLY You cant write against a read only replica.这是合理的约束。如果允许从节点写入该节点的数据会和主节点产生分歧后续增量命令到达时要么产生覆盖要么无法合并最终导致数据不一致和复制中断。因此生产环境永远不要开启从节点的写入能力除非短时间用它做特殊的数据修复。5.2 读取从节点数据时要注意复制延迟主从复制是异步复制主节点执行完写命令后立即返回成功不等从节点确认。因此在极端高负载或主从网络异常时从节点的数据可能落后于主节点。对于一致性要求很高的读请求例如用户刚提交订单后立即查询订单状态从从节点读取可能读到旧数据。应对策略有三种关键读请求强制走主节点普通读走从节点。常见做法是在业务代码里根据读敏感度路由到不同 Redis 节点。使用 WAIT 命令等待复制完成。WAIT numreplicas timeout可以等待至少 N 个从节点确认复制完成但这会牺牲一部分写入响应时间。接受较短时间的最终一致性适合读多写少且数据变更允许有秒级延迟的场景比如商品详情、配置项、非核心列表数据。5.3 从节点也可以有自己的从节点吗可以。Redis 支持链式复制即从节点 A 作为从节点 B 的主节点。这样做的好处是减轻主节点的推送压力让主节点只复制给少数几个“一级从节点”再由一级从节点转发给更下游的节点。缺点是链路变长任何一层延迟都会传导到更下游监控也更复杂。中小型业务规模不推荐链式复制直接让主节点连接全部从节点即可。6. 常见问题排查复制链路不可能永远一帆风顺主从复制最常见的问题集中在全量同步风暴、断线重连退化为全量同步、认证失败和主从数据延迟。下面按现象、原因、检查方式、解决方案逐一整理。6.1 从节点一直处于 connect 状态现象info replication中从节点stateconnect或stateconnecting始终无法变为 online。检查顺序docker exec -it redis-replica1 redis-cli ping docker exec -it redis-replica1 redis-cli info replication docker logs redis-replica1可能原因从节点配置中replicaof的地址不对或者主节点端口没开放。主节点开启protected-mode yes但没有配置密码也没有在 bind 列表中允许从节点 IP。容器间网络不通Docker Compose 中服务名无法解析。主节点配置了requirepass从节点没有配置masterauth。解决方案检查replicaof主机名和端口是否能在容器内 ping 通。如果主节点启用了密码从节点必须增加masterauth yourpassword6.2 网络抖动后大量从节点做全量同步现象主节点网络短暂不可用后多个从节点同时重新连接主节点 CPU 升高带宽打满所有从节点开始全量同步。原因断线时间较长从节点请求的偏移量已经不在 repl_backlog 缓冲区范围内。主节点只能推送 RDB 文件导致大量带宽消耗。如果同时有多个从节点重连就会出现“复制风暴”。解决方案适当调大repl-backlog-size例如从 1MB 调到 64MB 或 128MB让断线重连能够落在增量范围内。减少从节点直接连接主节点的数量使用链式复制Tree Replication分散压力。在业务低峰期处理大规模重启避免多个从节点同时冷启动。6.3 主从数据延迟越来越大现象info replication显示从节点 lag 值不断增大业务读取从节点总是拿到旧数据。检查方式查看主从之间网络延迟和丢包率用ping、redis-cli --latency观察。查看主节点是否执行了大量阻塞性命令例如KEYS *、HGETALL大 key 或SORT大集合。检查从节点是否在同时执行 RDB 快照或 AOF 重写导致从节点处理命令缓慢。解决方案避免在主节点执行阻塞命令改用SCAN系列命令遍历 key。把持久化操作放到从节点执行但要注意从节点性能。如果业务对一致性要求高可以引入 WAIT 或让关键读请求走主节点。6.4 主节点挂掉后想手动切换到从节点现象主节点 ip 不可用希望立刻把某个从节点提升为新主节点继续写入。操作方式docker exec -it redis-replica1 redis-cli REPLICAOF NO ONE执行后这个节点会成为新的主节点开始接收写请求。其他从节点需要重新指向新主节点docker exec -it redis-replica2 redis-cli REPLICAOF redis-replica1 6379这里直接写容器服务名是因为仍处于 Docker 网络内。如果宿主机访问要使用映射后的 IP 和端口。生产环境不建议手动执行这套操作而应该交给 Sentinl 自动完成。手动操作容易忘记切换客户端连接配置而且难以保证数据不丢失。新主节点没有同步到的最后写命令在重建主从后可能永久丢失。6.5 主从复制常见问题速查表问题现象常见原因检查方式处理建议从节点 connect无法同步网络不通、认证失败ping、查看日志修正 replicaof配置 masterauth全量同步频繁repl_backlog 太小查看同步日志调大 repl-backlog-size主从延迟大网络差、大 key、从节点负载高查看 lag、连接数分散读压力、禁止大 key 操作从节点写入报 READONLY只读配置检查 slave-read-only不要直接写从节点切换后数据丢失主节点故障前数据未同步对比 repl offset使用哨兵高可用挂多个从节点6.6 常见坑清单坑一把主节点配置成protected-mode no后直接暴露公网导致 Redis 被扫描攻击。学习环境可以这样用但生产环境必须配置bind白名单和requirepass。坑二从节点只配置replicaof忘记配置masterauth。主节点本来有密码从节点连接时被拒绝日志里会反复出现MASTER - REPLICA sync started:和Authentication failed。坑三全量同步过程中直接查看从节点数据发现数据不全以为同步失败了。其实从节点加载 RDB 需要时间加载完成前部分 key 是不存在的。等待info replication的 offset 追上主节点后再验证。7. 从主从复制走向哨兵和集群什么场景该用什么方案主从复制本身已经能让 Redis 从“单点开发工具”变成“可扩展的缓存层”。但它不是终态不同业务规模需要不同架构。7.1 主从复制、哨兵、集群三种形态对比形态解决核心问题自动故障转移数据分片适用规模单节点本地缓存无无开发、测试主从复制读扩展、数据热备无无中小型项目读多写少哨兵 主从主节点自动故障转移有无需要高可用数据量在一台机器可承受Redis Cluster数据分片 高可用有16384 个槽位数据量大单机内存不足选择依据很简单如果一台 Redis 实例内存足够、查询量没有把单个实例压满使用哨兵 主从是最稳妥的高可用方案。当内存容量超过单台服务器、单实例写入 QPS 成为瓶颈才需要 Cluster 分片。7.2 哨兵在主从基础上做了什么哨兵Sentinel是一个独立进程通过订阅主节点的__sentinel__:hello频道和定期 PING 来监控主从节点状态。当主节点被多数哨兵判定为主观下线就会触发故障转移流程自动把一个从节点提升为主节点并通知其他从节点和老客户端更新连接目标。在主从基础上部署哨兵复杂度会增加但换来了真正的自动恢复能力。实际生产环境至少需要 3 个哨兵节点保证在形成多数派时不会出现双主脑裂。7.3 除了高可用还要做哪些配套即使部署了主从和哨兵Redis 依然需要外部配套。持久化策略不能只依赖复制。复制没有落盘时重启后数据可能为空。需要监控主从延迟、内存使用、连接数和慢查询一旦异常能及时告警。客户端要支持故障转移时的重连和路由更新否则主备切换后业务请求仍然指向旧主节点。8. 生产环境的主从复制最佳实践8.1 配置层面的核心建议主节点配置主要注意开启requirepass和masterauth从节点必须配置相同密码。protected-mode在生产环境保持默认的 yes并通过bind限制来源 IP。根据网络状况调整repl-backlog-size建议从 64MB 起调观察断线重连是否触发全量同步。避免在主节点上执行KEYS *、SMEMBERS大集合这类阻塞命令复制进程也会受影响。从节点配置主要注意保持replica-read-only yes。如果从节点承担持久化备份任务要确保磁盘空间充足。从节点数量不建议过多一个主节点挂 2 到 3 个从节点通常足够。更多从节点会加重主节点的发送压力。8.2 监控指标清单监控项命令/来源预警条件处理建议复制 offsetINFO replicationoffset 差距持续增大检查网络和从节点负载lagINFO replicationlag 大于 5 秒检查心跳和命令执行耗时从节点状态INFO replicationstate 非 online查看日志并重建同步内存使用INFO memoryused_memory 超过 maxmemory 80%清理过期 key 或扩容磁盘占用系统 df超过 80%清理 RDB/AOF 冗余文件大 key 数量redis-cli --bigkeys存在超大 key拆分为更小 key8.3 发布前检查清单主从节点 Redis 版本保持一致避免命令行为和复制协议差异。检查所有从节点是否都已配置replicaof且没有遗漏masterauth。确认防火墙和安全组放行 6379/6380/6381 端口且仅允许需要的来源 IP。验证主节点挂掉后从节点读写表现是否符合预期。确认哨兵、客户端路由、监控告警都已经部署不只是手动切换。测试一次完整的数据插入和从节点读取流程确认写入后能立刻读取到。8.4 给新手的练习建议先把一主一从跑通观察INFO replication的输出变化。然后人为关闭主节点看从节点的REPLICAOF NO ONE切换过程。接着部署两个从节点用另一个从节点重新指向新主节点。最后再引入哨兵观察自动故障转移的执行流程。主从复制是最能体现 Redis 高可用设计思路的机制把这一层弄清楚后续学哨兵时只是增加一个监控角色学集群时只需要额外理解槽位分配逻辑学习曲线会平滑很多。
返回列表