
先聊个特别常见的场景你手里的Redis服务平时跑得挺稳直到某一天它突然挂了然后整个应用跟着一起不可用排查半天发现就是单点故障——一台Redis扛所有读写挂了就全没了。这时候你就知道Redis主从节点这套东西有多重要了。所谓主从节点本质就是一台主节点master负责接收写请求一个或多个从节点slave/replica负责同步主节点的数据对外提供备份、读流量分摊和故障兜底能力。它解决的问题很直接单点故障、读压力过大、误操作后数据恢复。无论你是刚接触Redis想做高可用还是已经在生产环境里被故障坑过想补课这篇文章都值得你花十分钟看完。我会把原理、部署、验证、坑这几块一次讲透并且给出可以直接照抄的命令和配置。1. 主从节点到底解决了什么先认清这三个核心价值1.1 单点故障一台机器扛所有压力的大坑Redis默认是单机运行的所有数据都存在一台机器的内存里。平时看着没什么问题可一旦这台机器陷入僵死、内存被写爆或者所在宿主机出现异常整个服务立马不可用。更麻烦的是数据如果开启了持久化还好最多丢一部分数据如果没有合理配置持久化那连家底都可能一次性清零。这就是主从架构的第一个核心作用给数据做实时副本。主节点照常处理业务请求从节点在后台持续同步数据。一旦主节点出问题你手里至少还有一份完整的数据副本不至于两手空空。比如我在实际运维中遇到过Redis所在磁盘IO频繁打满的情况主节点阻塞将近半分钟如果没有从节点那段时间所有缓存请求全部直接穿透到数据库依赖缓存的接口全崩。1.2 读写分离把读压力从主节点上拆走Redis本身就是单线程模型所有命令在它内部是串行执行的同一个时刻只能处理一条命令。虽然它处理单条命令的速度很快但扛不住大量并发读。如果业务又是典型的读多写少比如商品详情、用户信息、配置中心这类场景大量读请求全部压在同一个实例上CPU和网络带宽很容易成为瓶颈。主从架构天然适合读写分离。写操作、事务性操作、需要强一致性的读全部走主节点能接受短暂延迟的读操作走从节点。比如一个秒杀商品页库存、状态等关键写操作只打到master商品介绍、评论列表这些非关键读就可以分摊到多台slave上。这样一来主节点的压力就小了很多整体吞吐量能提升好几倍。1.3 数据容灾多一份副本多一层保险除了故障和生产压力人总是会犯错的。比如凌晨三四点在线上环境误执行了一条FLUSHALL或者DEL了一个大key发现的时候数据已经被清了。如果只有一台Redis且开启了持久化还有机会通过RDB或AOF恢复如果持久化策略写得不理想或者刚巧在这段时间内覆盖了旧快照那就真的回天乏术了。但如果你配了主从从节点上还保留着故障发生前的数据副本你可以从从节点快速恢复数据可以把从节点临时提升为主节点继续服务也可以直接在从节点上执行BGSAVE拿到一份最新的RDB文件。说白了主从架构就是给数据多买了一重保险关键时刻救命用的。2. 主从复制的核心原理这几个词搞不明白肯定踩坑2.1 数据是怎么从master流向slave的Redis主从复制的数据流从整体上看可以拆成三个阶段握手建立连接、数据快照传输、增量命令传播。从节点启动后会主动连接主节点如果配置了密码还需要带上认证信息。握手成功后从节点发送同步请求PSYNC replid offset告诉主节点自己希望从哪个复制ID、哪个偏移量开始同步。如果是第一次同步主节点会比较复制ID和偏移量发现无法衔接就会触发一次全量同步。全量同步的核心动作是主节点执行BGSAVE生成RDB快照然后把RDB文件发送给从节点从节点清空旧数据后载入这份快照。为什么是全量因为从节点没有任何历史上下文主节点无法知道从节点缺了哪些数据最稳妥的办法就是把当前完整数据打包传过去。等RDB加载完成主从之间的数据才算对齐到某一时刻。但注意在BGSAVE和RDB传输期间写命令还在不断进来。主节点不会丢下这些命令不管而是把它们写入了复制积压缓冲区和自己的AOF缓冲随后以命令流的方式持续转发给从节点。这一阶段也叫命令传播command propagating从节点只要保证跟上主节点的节奏就能长期保持数据一致。2.2 全量同步与增量同步的分界线很多人在面试里被问过“PSYNC和SYNC有什么区别”其实就是全量同步与增量同步的划分。早期版本只有SYNC每次断线重连都要重新传一次全量RDB数据量大时效率非常低。后来引入PSYNC支持部分重同步也就是只补传连接断开期间缺失的命令。判断走全量还是增量核心看两个信息一个是主节点的replid复制ID另一个是复制偏移量offset。从节点发起PSYNC时会带上自己记录的主节点replid和已接收的偏移量。主节点收到后先比对replid是否一致不一致说明从节点之前连的不是自己或者自己重启过导致复制ID变化这时只能全量同步如果replid一致再看偏移量是否还在复制积压缓冲区repl-backlog里。如果从节点的偏移量落后不多主节点可以直接从积压缓冲区里取出缺失的命令发送过去网络传输量就很小从节点也能快速追平。但如果从节点落后太多积压缓冲区已经覆盖不到那个偏移量了主节点没办法补全缺失部分只能重新来一次全量同步。这里有一个容易忽略的点repl-backlog-size默认是1MB如果主节点写流量很大几秒钟就能把缓冲区写满。一旦从节点断线超过这个窗口再重连就得全量同步面对几十GB的数据网络和磁盘都吃得很难受。所以生产环境里这个值要根据写QPS和平均命令大小去估算。2.3 复制偏移量、run_id和积压缓冲区到底怎么配合要真正理解主从复制必须把三个核心概念串起来。第一个是replid也叫复制ID相当于主节点的身份标识。Redis每次启动时会随机生成一个新的41位十六进制字符串从节点会记录自己当前跟随的主节点replid。如果主节点发生了重启replid就变了老从节点再连上来会发现replid对不上被迫触发全量同步。这也是为什么Redis主节点重启这件事在生产环境里要尽量谨慎。第二个是复制偏移量主节点每发送一条命令就会给这条命令打上一个字节级偏移量从节点每接收并应用一条命令也会记录自己当前的偏移量。两边一对比就知道从节点落后了多少。通过INFO replication命令里的master_repl_offset和slave_repl_offset你可以很直观地看到主从之间的差距。第三个是复制积压缓冲区这是主节点内存里的一个环形队列用来保存最近传播过的写命令专门给断线重连的从节点补数据用的。因为它是环形覆盖的新的写命令会不断覆盖最旧的数据所以它能覆盖多远的过去完全取决于缓冲区大小和写命令的流量。你可以粗略估算假设平均每条写命令100字节每秒写入2000条那每秒产生大约200KB数据默认1MB的缓冲区大约只能覆盖5秒的断线窗口。这个数字是不是有点惊到你了。3. 亲手搭一套主从两种部署方式完整实操3.1 Docker快速部署Redis主从环境现在是容器化时代本地验证主从架构最方便的方式就是用Docker不需要去系统里编译和排查依赖几分钟就能起来一套。假设你已经装好了Docker先创建一个自定义网络让两个容器可以通过容器名互相访问docker network create redis-net然后启动主节点容器docker run -d --name redis-master \ --network redis-net \ -p 6379:6379 \ redis:7.0再启动从节点容器关键是追加--replicaof参数告诉它谁才是masterdocker run -d --name redis-slave \ --network redis-net \ -p 6380:6379 \ redis:7.0 \ redis-server --replicaof redis-master 6379启动之后进到从节点容器里确认状态docker exec -it redis-slave redis-cli info replication如果看到role:slave、master_link_status:up说明主从已经建立。这里提醒一下Redis 5.0以后官方把slaveof改成了replicaof虽然老的slaveof命令仍然兼容但新的配置里建议直接用replicaof。如果主节点设置了密码从节点启动时还要带上对应的认证信息否则连接会被拒绝redis-server --replicaof redis-master 6379 --masterauth yourpassword3.2 Linux下源码包部署Redis主从生产环境里很多机器上的Redis并不是容器部署的而是源码包直接编译安装的。这里说一下最常见的源码安装方式。先去Redis官网或者通过wget下载源码包比如7.0版本wget https://download.redis.io/releases/redis-7.0.10.tar.gz tar -zxvf redis-7.0.10.tar.gz cd redis-7.0.10 make make install编译完成后把配置文件复制出来分别准备master和slave两份配置。master节点只管监听端口和数据持久化相关配置通常不需要额外修改。从节点的核心配置是这一行replicaof 192.168.1.10 6379放在redis-slave.conf里然后启动redis-server /etc/redis-slave.conf如果从节点和主节点不在同一网段或者有防火墙策略记得保证从节点能通过6379端口访问到主节点。还有许多云环境下主节点绑定了本机内网IP从节点在另一台机器上访问内网IP是正常的不要拿外网IP去试。3.3 验证从节点连接状态的正确姿势搭建完成后怎么判断主从是否真的工作正常很多人只是看主从节点进程起来了就觉得万事大吉但进程活着不代表复制是通的。用redis-cli连接任意节点执行redis-cli -p 6379 info replication输出里重点关注这几个字段role当前节点的角色master或者slaveconnected_slaves主节点视角下已连接的从节点数量slave0从节点列表包含IP、端口、状态、偏移量master_host、master_port从节点视角下master的地址和端口master_link_status从节点与master的连接状态up为正常down为断开master_last_io_seconds_ago多久之前和master有过IO交互如果持续增长说明同步可能卡住了我在验证时还会做一个动作在主节点上写一个测试key然后立刻到从节点上去读取。redis-cli -p 6379 set sync_test ok redis-cli -p 6380 get sync_test能读到说明数据流是通的。不过要注意主从复制是异步的写入后不是百分百立刻能在从节点读到正常场景下延迟是毫秒级但严格意义上你写完后立即去读从节点仍然可能读不到这在设计主从架构时要提前知道。4. 主从架构的高级玩法与应用场景4.1 一主多从搭建标准的读写分离架构生产环境最经典的主从部署就是一主多从比如一台master加两台slave。master负责写入slave负责处理读请求这样既分摊了读压力也保证了数据冗余。搭建方式很简单再启动一个slave容器或实例给它配置同样的replicaof指向master即可。需要注意的是多个从节点同时触发全量同步时会对主节点产生很大的瞬时压力。主节点要同时执行BGSAVE并分发多份RDB数据内存、磁盘IO和网络带宽都可能被瞬间打满。所以不要一次性把十个从节点同时挂上去分批加隔几秒再加一个能有效降低复制风暴风险。读写分离场景下还要考虑客户端的路由策略。你的应用里要有明确的读写分离逻辑比如Spring RedisTemplate可以把读请求指向从节点、写请求指向主节点。但别忽略一个重要问题Redis主从复制是异步的从节点上的数据永远可能比主节点旧几毫秒甚至更久。如果业务对数据一致性要求苛刻比如读后写、写后立刻读同一份数据这类请求必须强制走主节点否则可能出现刚写入却读不到的情况。4.2 级联复制解决从节点数量过多的延迟问题当从节点数量很多时每个从节点都直接从主节点同步数据主节点既要承担业务写入又要向所有从节点分发命令网络带宽和CPU开销都会上升。这时候可以采用级联复制。所谓级联就是某个从节点同时作为更下一层从节点的master。架构上变成主节点master从节点slave-1同时作为下一层的master从节点slave-1-1从节点slave-1-2从节点slave-2配置方式很简单在slave-1上不仅配置replicaof master_host 6379同时允许其他从节点连接它下一层从节点配置replicaof slave-1_host slave-1_port即可。级联模式的好处是能显著减轻主节点的复制压力数据从一个节点向多个节点扇出适合从节点非常多、或者跨机房的场景。但劣势也很明显链路变长后底层从节点的数据延迟会进一步加大。如果最底层的从节点承载了实时性要求较高的读请求要小心评估延迟是否在接受范围内。4.3 加一层哨兵从主从迈向真正的高可用讲到这里必须提一嘴单纯的主从架构并不等于高可用。原因很多人会忽略如果没有额外的故障转移机制当主节点宕机时从节点只会一直尝试重连主节点并不会自动把自己提升为新的master。这时候整个Redis服务实际上是处于只读状态所有写请求都会失败。解决这个问题的标准方案是Redis Sentinel哨兵。哨兵进程会持续监控主从节点的健康状态当主节点被判定为客观下线后哨兵会从从节点中选举一个提升为新的master然后通知其他从节点去复制新master。应用端通过哨兵获取当前master地址就可以在故障转移后自动切换。主从加哨兵是Redis高可用领域用得最广的一套组合。它比手动切换可靠得多也比Cluster集群更简单适合大多数中小规模的业务。不过哨兵本身的部署也有讲究最少要三个哨兵实例且独立部署避免哨兵自身成为单点。这部分坑也比较多后面可以单独写一篇聊哨兵的选主过程这里先把主从的地基打牢。5. 主从环境下常见问题与排查经验实录5.1 主从数据不一致先查这四个地方主从之间数据偶尔不一致是运维Redis时最常碰到的困扰之一。这里整理一下我排查过的典型原因和对应思路。第一网络延迟或带宽瓶颈。从节点接收命令的速度赶不上主节点产生命令的速度导致偏移量差距越来越大。用INFO replication对比两边的offset如果差值持续增长优先排查网络延迟、主从机器负载以及是否有大key在传播。第二repl-backlog-size设置得太小。断线重连时如果发现offset已经不在积压缓冲区范围内会触发全量同步。全量同步期间主节点依然在处理写命令但从节点只能在加载完RDB后继续接收新增命令这期间数据窗口较大。解决方案是调大repl-backlog-size比如从默认的1MB调整到256MB甚至1GB视写入量而定。第三从节点有本地写入。Redis从5.0之后从节点默认是replica-read-only yes只允许读不允许写。但如果你手工改成了no从节点上被写入的数据这些值不会同步回主节点但会被后续主节点的命令覆盖。这种不一致通常很隐蔽查配置就能发现。第四过期key的处理。Redis主从对过期key的处理机制比较特殊主节点在key过期时会生成一个DEL命令传播给从节点从节点依据这个DEL进行删除。但如果从节点上有应用直接读取一个在本地已过期的keyRedis的惰性删除策略会在从节点读操作时发现过期并删除它。在某些极端时间窗口内从节点可能短暂暴露已过期的数据。对于需要严格一致的场景这个特性必须心里有数。5.2 从节点写入报错READONLY不是配置坏了很多人头一次连接从节点想写入数据时会看到这样一行报错(error) READONLY You cant write against a read only replica.这个报错不是故障而是Redis的保护机制在起作用。从7.0开始从节点默认是只读的不允许应用写入。这样设计的目的就是为了防止从节点上的本地数据和主节点冲突避免服务无意的写入造成数据混乱。如果你确实需要临时在从节点上执行写操作可以在从节点的配置里改replica-read-only no然后重启或者用CONFIG SET replica-read-only no动态调整。但这是极其不推荐的做法。一旦从节点写入没有同步回主节点后续主节点对同一个key的更新会直接覆盖掉你的写入数据一致性无从谈起。我在生产环境里从没见过哪个合理场景需要让从节点开放的大多数“从节点写不进去”的问题本质上都是客户端路由配置错误把应该走master的写请求发到了slave。5.3 主节点宕机后从节点不会自动上位这是主从架构使用中误解最深的一个点。很多新手会以为master挂了以后slave会自动接管成为新的master。实际上不会。没有哨兵的情况下master宕机后slave会一直处于等待重连的状态它只是保留数据副本但不会主动变成主节点也不会接受写请求。这时候你只能手动处理比如用REPLICAOF NO ONE命令把一个从节点提升为新的主节点其他从节点再切换到这个新主节点。整个过程需要人工介入而且应用的写入端也要同步切换连接地址业务中断时间取决于你的响应速度。如果希望故障转移不再依赖人工就必须引入哨兵Sentinel。哨兵会监控master的状态确认客观下线后发起故障转移从中选出一个新任master。这套机制能自动完成整个切换过程应用端通过哨兵感知新master地址。所以主从本身是数据备份和数据分发方案主从加哨兵才是高可用方案逻辑上这两个概念不要混为一谈。5.4 处理复制风暴、大key卡顿和重启元凶最后再分享几个我在实际运维中踩过且算是比较高频的坑。第一个是大key复制导致的同步卡顿。如果Redis里存了一个几百MB的list或hash主节点传播这个key时会一次性把整个value序列化后发送从节点也要一次性加载并写入内存。这个过程可能造成网络传输阻塞、从节点命令处理阻塞甚至触发全量同步超时。主从架构下必须在上层就控制好单个key的尺寸比如大集合拆分成多个小key或者用其他存储承载大对象。第二个是主节点重启引发的连锁反应。前面说了主节点重启会生成新的replid所有从节点都会因此触发全量同步。如果你有几十个从节点和级联节点一次重启可能引发一轮复制风暴把整个集群流量打满。我在生产环境里换master配置时都会提前做一次主从切换避免直接重启master。第三个是缓冲区配置。对写并发较高的场景repl-backlog-size默认值远远不够。我通常会把master节点的repl-backlog-size设到512MB以上配合min-replicas-to-write和min-replicas-max-lag这类参数在主节点异常时不至于让不可靠的副本继续接收写流量这是生产环境里很实用的一招。在实际操作中我特别建议养成一个习惯在任何主从节点上执行破坏性命令之前先用INFO replication确认自己的角色和当前拓扑再动手。很多时候你以为你连的是master实际上配了一个从节点结果一条FLUSHALL直接把线上缓存清了这种事故光想想都冒冷汗。Redis主从节点这套东西认真梳理下来其实并不复杂核心就是数据同步、角色分工、故障兜底这几件事。真刀真枪部署一遍、亲手把master打到故障再完成一次手动切换比看十篇文章都管用。等主从这一层跑得足够稳了再往上加哨兵、演进到Cluster心里就有底了。