当前位置: 首页 > news >正文

【Redis从入门到精通】第39篇:Redis主从复制——数据如何在主从节点间同步

上一篇【第38篇】serverCron——Redis的"心跳"定时任务干了哪些活
下一篇【第40篇】旧版复制的硬伤——Redis 2.8之前为什么会反复全量同步


假设你开了一家奶茶店,生意火爆到一个人忙不过来。你请了个助手,但是菜单(数据)只在你手里。客人点单你得自己查,做完还得自己喊号,助手只能站着发愣。这时你掏出一个小本本,每次有菜单变更(比如草莓断货了)就抄一份给助手同步——这就是主从复制的雏形。Redis的主从复制也是这个道理:一台机器顶不住?加一台,把数据"复制"过去。

主从复制的三大用途

在动手搭建之前,先搞清楚"为什么要主从":

主从复制的核心价值 ┌─────────────────────────────────────────────────────────┐ │ │ │ 1. 读写分离 2. 数据冗余 │ │ ┌──────┐ ┌──────┐ │ │ │ 主库 │ 写 │ 主库 │ 挂了! │ │ └──┬───┘ └──────┘ │ │ │ │ │ │ ┌──┴───────────────────┐ │ 从库顶上 │ │ │ │ │ │ ▼ │ │ ▼ ▼ ▼ │ ┌──────┐ │ │ 从1 从2 从3 │ │ 从库 │ 数据还在! │ │ (读) (读) (读) │ └──────┘ │ │ │ │ │ 3. 故障恢复 │ │ │ 主库挂了 → 选一个从库 → │ │ │ 提升为新主库 → 服务恢复 │ │ └─────────────────────────────────────────────────────────┘

读写分离:主库负责写,多个从库分担读压力。如果你的业务是"读多写少"(大部分互联网应用都是这样),加几个从库就能线性扩展读吞吐量。

数据冗余:相当于数据有了"异地备份"。主库的数据完整地拷贝到了从库,主库挂了,至少数据还在。

故障恢复:主库宕机后,可以从从库中选一个提升为新的主库,业务切换到新主库继续运行。这是Redis高可用的基础,配合Sentinel可以实现自动故障转移。

主从复制的拓扑结构

Redis支持灵活的拓扑组织方式:

拓扑结构对比 【一主多从】 【级联复制】 ┌─────────┐ ┌─────────┐ │ 主库 │ │ 主库 │ └──┬──┬───┘ └───┬─────┘ │ │ │ │ ┌──▼──┐ ┌──▼──┐ ┌──▼──┐ ┌───▼───┐ │ 从1 │ │ 从2 │ │ 从3 │ │ 从1 │ └─────┘ └─────┘ └─────┘ └───┬───┘ │ ┌─────▼─────┐ │ 从1-子 │ ← 从库的从库 └───────────┘ 优点:管理简单、主库负载低 优点:减轻主库的复制压力 缺点:所有复制流量走主库 缺点:链路越长延迟越大

一主多从适合大多数场景。级联复制适合从库非常多的情况(比如几十个从库),让主库只复制给几个"一级从库",再由它们分发给更多的"二级从库"。

搭建你的第一个主从复制

最简配置

最少两台机器(或同一台机器两个端口),配置文件只需要一行:

# 从库的redis.conf replicaof 192.168.1.100 6379 # 或者旧版语法: # slaveof 192.168.1.100 6379

注意:replicaof是 Redis 5.0 引入的新命令,用它替代了带有历史包袱的slaveof。老版本(2.8~4.x)只能用slaveof。功能上两者完全等价。

带认证的配置

生产环境一般都会设置密码:

# 主库 redis.conf requirepass mypassword123 # 从库 redis.conf masterauth mypassword123 # 从库用来连接主库的密码 replicaof 192.168.1.100 6379

如果主库设置了requirepass,从库必须配置对应的masterauth,否则连接会被拒绝。

从库只读

# 查看从库只读设置redis-cli CONFIG GET replica-read-only# 1) "replica-read-only"# 2) "yes"# 从库默认不接受写命令redis-cli-p6380SETtestvalue# (error) READONLY You can't write against a read only replica.

这是很好的安全设计——如果业务代码不小心向从库发了写请求,Redis会直接拒绝,避免主从数据不一致。

运行时切换主从关系

不需要重启,REPLICAOF命令可以动态地改变主从关系:

# 让当前节点成为192.168.1.100:6379的从库redis-cli-p6380REPLICAOF192.168.1.1006379# 让当前节点脱离主从关系,升级为独立主库redis-cli-p6380REPLICAOF NO ONE

REPLICAOF NO ONE在故障转移时特别有用——主库挂了,在合适的从库上执行这个命令,它就成了新的主库,可以接收写请求。

复制过程的两个阶段

主从复制不是一次性把数据搬过去就完了,它分为两个相辅相成的阶段:

复制两阶段 ┌──────────────────────────────────────────────────────┐ │ │ │ Phase 1:初次同步(全量复制) │ │ ┌──────┐ ┌──────┐ │ │ │ 主库 │ ═══ RDB文件 ═══→ │ 从库 │ │ │ └──┬───┘ └──┬───┘ │ │ │ │ │ │ │ ① 主库fork出子进程 │ │ │ │ ② 子进程生成RDB快照 │ │ │ │ ③ RDB发给从库 │ ④ 从库清空自身数据 │ │ │ ⑤ 主库把"传输期间" │ ⑥ 从库载入RDB │ │ │ 的写命令缓存发过去 │ ⑦ 从库执行缓存命令│ │ │ │ │ │ Phase 2:增量同步(命令传播) │ │ ┌──────┐ ┌──────┐ │ │ │ 主库 │ ═══ 写命令 ═══→ │ 从库 │ │ │ │ SET │ ┌──→│ SET │ │ │ │ DEL │ │ │ DEL │ │ │ │ INCR │ │ │ INCR │ │ │ └──────┘ │ └──────┘ │ │ │ │ │ ← 异步,主库不等从库确认 ← │ │ │ └──────────────────────────────────────────────────────┘

Phase 1:全量复制(初次握手)

当从库第一次连接到主库时,主从之间数据完全不一致,必须做一次全量同步。这是整个复制过程中最"重"的操作:

  1. 从库向主库发送PSYNC ? -1(首次同步的特殊标记)
  2. 主库收到后,执行BGSAVE生成RDB快照
  3. RDB生成期间,主库把收到的所有写命令记录到一个"复制缓冲区"里
  4. RDB文件生成完毕,主库把文件发送给从库
  5. 从库收到RDB后,先清空自身所有数据,然后载入RDB
  6. 载入完成后,主库把"复制缓冲区"中的命令发送给从库
  7. 从库执行这些命令,追平RDB生成到传输完成之间的"时间差"
  8. 全量复制完成,进入增量复制阶段

这个阶段的时间开销取决于数据量。一个简单的估算公式:

数据量RDB生成时间RDB传输时间从库恢复时间总计(估算)
100MB1~3s1~5s(千兆网)5~10s7~18s
1GB5~15s10~30s30~60s45~105s
10GB30~60s60~180s5~10min7~13min
100GB3~10min10~30min30~60min45~100min

Phase 2:增量复制(命令传播)

全量复制完成后,主从之间通过"命令传播"保持数据一致。主库每执行一条写命令,就把这条命令广播给所有从库:

// propagate() 简化逻辑voidpropagate(structredisCommand*cmd,intdbid,robj**argv,intargc){// 1. 如果开启了AOF,把命令追加到AOF缓冲区if(server.aof_state!=AOF_OFF)feedAppendOnlyFile(cmd,dbid,argv,argc);// 2. 如果有从库,把命令追加到复制缓冲区if(listLength(server.slaves))replicationFeedSlaves(server.slaves,dbid,argv,argc);}

异步复制的代价

一个需要认真对待的事实:主库不等待从库确认写命令。主库执行完写命令后立即返回给客户端,复制操作在后台异步进行。这带来的好处是:从库的网络延迟不会影响主库的写入性能。但代价是:

异步复制的数据丢失风险 时间线: t0: 客户端 SET key "important_data" t1: 主库执行成功,返回OK给客户端 t2: 主库把命令发送给从库(异步) t3: 主库宕机!(天有不测风云) 结果:从库永远不会收到"SET key important_data" "important_data" 永远丢失 (数据丢失窗口 ≈ 主库宕机那一刻到从库追平数据的时间差)

复制延迟监控

因为异步复制,从库的数据天然比主库"老"一点。这个延迟有多大呢?

# 在主库上查看复制状态redis-cli INFO replication# 输出示例:# # Replication# role:master# connected_slaves:2# slave0:ip=192.168.1.101,port=6379,state=online,offset=1234567,lag=1# slave1:ip=192.168.1.102,port=6379,state=online,offset=1234560,lag=3# master_repl_offset:1234567# 在从库上查看redis-cli-p6380INFO replication# 输出示例:# # Replication# role:slave# master_host:192.168.1.100# 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

关键字段解读:

字段含义健康判断
master_repl_offset主库的复制偏移量(它是一个全局递增的计数器)对比slave的offset
slave_repl_offset从库当前的复制偏移量master_repl_offset - slave_repl_offset就是延迟的数据量
lag从库延迟秒数(0表示几乎无延迟)lag > 5就值得关注了
master_link_status主从连接状态必须是up
master_sync_in_progress是否正在全量同步应该是0(除非正在初始化)
# 计算复制延迟(数据量)redis-cli-hmaster INFO replication|grepmaster_repl_offset|cut-d:-f2# 1234567redis-cli-hslave INFO replication|grepslave_repl_offset|cut-d:-f2# 1234467# 延迟 = 1234567 - 1234467 = 100字节(几乎可以忽略)

WAIT命令——给异步复制加一点同步

如果你有一笔"绝对不能丢"的写入,可以用WAIT命令:

# SET之后,等待至少1个从库确认复制,超时1000msSET important_key"critical_value"WAIT11000# (integer) 1 ← 1个从库已确认# 如果超时了还没等到足够确认数WAIT2100# (integer) 1 ← 只有1个从库确认(不满足2个的要求)

WAIT的语义:

  • 第一个参数numreplicas:需要等待多少个从库确认
  • 第二个参数timeout:超时时间(毫秒)
  • 返回值:实际确认的从库数量(可能小于numreplicas)

踩坑提示WAIT不是原子的!SETWAIT是两条独立的命令。在它们之间,主库仍可能宕机。如果需要强一致性,考虑使用Redis Sentinel + 客户端确认机制,或者直接上Redis Cluster。更重要的是——WAIT会让当前客户端连接阻塞,它会阻塞整个Redis的事件循环(单线程!),直到超时或足够多从库确认。在高并发场景下慎用。

读写分离中的注意事项

读写分离听起来很美好——主库写、从库读、吞吐翻倍。但实现起来有几个容易踩的坑:

读写分离的"童话 vs 现实" 童话: 现实: ┌──────┐ ┌──────┐ │ 主库 │──→写 │ 主库 │──→写 (100%) └──┬───┘ └──┬───┘ │ │ 异步复制 ┌──┴────────────┐ ┌────┴──────────┐ │ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ▼ 从1 从2 从3 从1 从2 从3 (33%) (33%) (34%) (延迟) (延迟) (延迟) "刚写入的数据查不到"

问题一:数据延迟

客户端刚刚写入主库,立刻去从库读——可能读不到!因为异步复制有延迟。

应对策略

  • 对实时性要求高的场景(如扣减库存后立即查询余额),读写都走主库。
  • 延迟可接受的场景(如推荐文章列表、历史订单),走从库。
  • 使用WAIT命令强制等待复制后再去从库读。

问题二:从库故障

如果某个从库挂了,客户端必须能感知并切换到其他从库或主库:

// 伪代码:连接池分离读写classRedisPool{JedisPoolmasterPool;// 主库连接池(写)List<JedisPool>slavePools;// 从库连接池(读)Stringget(Stringkey){// 随机选一个健康的从库JedisPoolpool=selectHealthySlave();if(pool==null){// 所有从库都挂了,降级到主库读pool=masterPool;}returnpool.getResource().get(key);}Stringset(Stringkey,Stringvalue){// 写永远走主库returnmasterPool.getResource().set(key,value);}}

问题三:主从数据不一致

如果主库用了RANDOMKEYSPOP等随机的命令,主从之间执行结果可能不同。虽然Redis主从复制传播的是命令(不是结果),保证了确定性命令的一致性,但你得清楚哪些命令在主从上表现不同。

总结

主从复制是Redis高可用的基石,理解它需要记住几个关键点:

维度要点
三大用途读写分离、数据冗余、故障恢复
拓扑结构一主多从(简单)、级联复制(减轻主库压力)
配置从库replicaof一行搞定,认证加masterauth
复制阶段全量复制(RDB)+ 增量复制(命令传播)
异步特性主库不等从库确认,可能丢数据
延迟监控INFO replication中的offset和lag
WAIT命令给异步复制加了一层"半同步"确认
读写分离注意数据延迟、从库故障切换、一致性保证

主从复制看起来不复杂,但要做到生产级稳定运行,还需要理解复制协议的演变——这就要说到旧版SYNC的那些硬伤了。


上一篇【第38篇】serverCron——Redis的"心跳"定时任务干了哪些活
下一篇【第40篇】旧版复制的硬伤——Redis 2.8之前为什么会反复全量同步


http://www.zskr.cn/news/1449615.html

相关文章:

  • 保姆级避坑指南:用imu_utils和Kalibr搞定T265双目+IMU联合标定(含报错全解)
  • TI TPS54824芯片调试血泪史:AGND与PGND分开铺铜,一个0Ω电阻救了我的板子
  • LLM微调实验失控?用Weights Biases+MLflow+Kubeflow构建可审计、可回滚、可合规的AI实验闭环(附生产环境配置清单)
  • 保姆级教程:在Android 13源码里预装可卸载的微信/抖音(附完整Shell脚本)
  • 从‘网络退化’到‘恒等映射’:深入浅出图解ResNet残差连接,为什么它能救活超深网络?
  • 企业文件同步引擎的架构设计:从rsync到实时增量同步
  • 别再只用CUDA_VISIBLE_DEVICES了!PyTorch和TensorFlow指定GPU的5种实战方法对比
  • 智能小车PCB实战:用AD24完成从原理图绘制到DRC检查的全过程解析
  • 扩散模型太随机?BBDM不够用?试试DDBM:一个模型搞定确定性与多样性
  • 福宝清朝姓氏历史整 理记录随笔2000年
  • 2026贵阳靠谱装企深度解析|福旺居装饰 高转介绍率背后硬实力 - 资讯纵览
  • 别再死记硬背了!用Librosa和Python实战,5分钟搞懂梅尔频谱(Mel Spectrogram)到底是个啥
  • 终极Dify工作流实战指南:7天从零构建企业级AI应用的完整方案
  • Perseus终极指南:3步快速解锁碧蓝航线全皮肤功能
  • 别再手动调参了!Halcon拟合直线/圆实战:用edges_sub_pix和fit_line_contour_xld搞定工业零件测量
  • 5分钟快速上手!用YUM在CentOS/RHEL 8一键部署PostgreSQL 16并配置远程访问
  • 2026 年 6 月教资免费题库避坑:真免费才是备考刚需 - 讲清楚了
  • 3分钟掌握Maya动画资源管理神器:Studio Library快速上手指南
  • 告别手动标注!用SAM+Labelme快速搞定YOLOv8-seg数据集(附完整脚本)
  • AI小白必看!从大模型到Token,我用费曼学习法揭秘AI底层概念
  • OpenCore Legacy Patcher终极指南:三步让老Mac焕发新生,免费运行最新macOS
  • AI Agent 爆款揭秘:将 LLM 转化为超级循环推理机器,轻松搞定复杂任务!
  • 2026 年 6 月教资题库免费实测:全免费才是真良心 - 讲清楚了
  • 破解索尼DMPORT接口:老音响改造通用音频输入全攻略
  • PhotoGIMP终极指南:让GIMP像Photoshop一样简单易用
  • Arduino RGB LED调光器:从电位器到PWM的嵌入式控制实践
  • Ascend C算子重构:从TBE到Native的高性能迁移实践
  • 别再盲目续费了!AI工具续约前必做的5项性价比审计(含自动化测算模板,限前200名领取)
  • 3个步骤快速上手:Czkawka帮你彻底清理电脑重复文件
  • GIT-base应用场景探索:图像描述、视觉问答与图像分类