Redis 主从复制:原理与实战
一、理论
整体机制
- 主从复制是异步 的、单向的:数据从主节点流向从节点,写操作只能在主节点上做,从节点默认只读。
- "异步"的意思是主节点把写命令发给从节点后并不等待确认,就直接回复客户端。好处是不影响主节点的性能,代价是主节点突然宕机时,最后一批还没传过去的写入会丢失。
- 主从复制本身不包含自动故障切换。它只解决数据冗余和读扩展,要做到主节点挂了自动顶上,还需要 Sentinel(哨兵)或 Redis Cluster。
一次完整的主从关系建立,会经历三件事:
- 建立连接、协商同步方式
- 全量同步(第一次连接时,或无法做增量同步时)
- 命令传播(之后持续进行的增量同步)
在学习 Redis 主从复制前,我们需要知道三个关键概念:
| 概念 | 含义 |
|---|---|
| run_id(复制 ID) | 每个 Redis 实例启动时生成的 40 位随机字符串,标识它这一次"生命期"。从节点重连时用它判断主节点是不是换过一个了 |
| 复制偏移量 offset | 主节点每发出 N 字节写命令就累加自己的 offset,从节点每收到也累加。双方 offset 一致,说明数据一致 |
| 复制积压缓冲区 backlog | 主节点维护的一个固定大小的环形缓冲区(由 repl-backlog-size 控制,默认 1MB),保存最近发出去的写命令,用来支持断线后只补差量 |
不同于 MySQL 的 mysqldump 全量备份与二进制日志持续同步,Redis 有其独特的机制。下面简单介绍一下 Redis 主从复制机制,有助于我们知道后续配置文件都在配置什么。
全量同步:原理是 RDB 快照
从节点在配置里写上 replicaof(旧版本叫 slaveof)之后:
- 从节点主动连接主节点,先发一个 PING 确认对方可用
- 如果主节点设了密码,从节点用配置里的
masterauth完成认证 - 从节点发送
REPLCONF listening-port <端口>,告诉主节点自己的端口 - 从节点发送
PSYNC ? -1,表示"我是第一次来,做全量同步" - 主节点回复
FULLRESYNC <run_id> <offset>,同时 fork 出一个子进程生成 RDB 快照 - 生成快照的这段时间里,主节点新收到的写命令会暂存到复制缓冲区里
- 主节点把 RDB 文件发送给从节点(配置
repl-diskless-sync yes时可以走无盘复制,直接把数据写进 socket,省掉落盘再读取的开销) - 从节点收到 RDB 后,先清空自己原有的数据,再加载这份快照
- 加载完成后,主节点把缓冲区里积攒的增量命令补发过去,双方 offset 对齐,数据就一致了
增量同步:围绕三个核心概念
增量备份主要围绕三个核心概念:run_id、offset 和 backlog 缓冲区。
链路断开后重连时,从节点会带上自己记录的 run_id 和 offset 再发一次 PSYNC:
- 如果主节点的 run_id 没变,并且 offset 之后的数据还都在 backlog 里,主节点就回复
+CONTINUE,只把缺失的那一小段命令补发给从节点,代价很低 - 如果主节点重启过(run_id 变了),或者断开时间太久、缺的数据已经超出 backlog 的范围,就只能重新做一次全量复制
所以把 repl-backlog-size 调大,能提高"断线重连只需增量同步"的概率。
心跳与超时:Redis 怎么确认连接断了
- 主节点默认每 10 秒向从节点发一次 PING,由
repl-ping-replica-period控制 - 从节点默认每秒向主节点回一个
REPLCONF ACK <offset>,汇报自己收到哪了 - 如果超过
repl-timeout(默认 60 秒)双方都没收到对方的消息,就判定连接断开 - 用
INFO replication看到的master_link_status:up,就是指这条链路是通的
几个实用结论
- 首次建立主从、或者断线太久导致 backlog 不够,都会触发全量复制,此时主节点要 fork 子进程并传输整份 RDB,内存和网络的开销都不小,尽量避开业务高峰
- 全量复制期间主节点默认不阻塞(由 fork 出的子进程干活),但 fork 的那一瞬间会有短暂卡顿,实例内存越大越明显
- 从节点默认是只读的(
replica-read-only yes),也可以再挂一层从节点,形成级联复制,用来分担主节点的复制压力 - 既然是异步复制,主从之间就存在毫秒到秒级的延迟,从节点上可能读到旧数据,对一致性要求高的读请求不要全部丢给从节点
二、实战
我们准备两台机器,一台机器为主,一台机器为从。
1. 安装 Redis
这里就不过多展开。
2. 配置主节点
安装好后配置主节点配置文件。默认路径可能因安装方式而有所不同,默认配置文件位于 /etc/redis/redis.conf 或 /etc/redis.conf:
bash
vim /etc/redis/redis.conf
需要修改 bind 和 protected-mode,确保 Redis 能够从外部访问(例如从从节点访问),将 protected-mode 设置为 no,并绑定主节点的 IP 地址。
ini
bind 0.0.0.0 # 允许任何 IP 访问
protected-mode no # 禁用保护模式
修改端口(可选):默认 Redis 使用 6379 端口,如果没有特殊需求,可以不修改。否则,修改为自己需要的端口。
修改配置后记得重启 Redis 服务。
然后检查一下主节点状态 ------ info replication 命令:
bash
redis-cli INFO replication
返回信息中,role 应为 master,表示当前是主节点。
3. 配置从节点
打开从节点配置文件:
bash
vim /etc/redis/redis.conf
设置主节点:在从节点的配置文件中,添加以下配置,指定主节点的 IP 地址。
设置主节点 + 端口,让从节点能定位到主节点:
ini
replicaof 172.22.4.3 6379 # Redis 5.0 版本以上使用
slaveof 172.22.4.3 6379 # Redis 5.0 版本以下使用
禁用保护模式:与主节点类似,关闭保护模式,确保从节点可以接受外部连接。
ini
protected-mode no
也是一样的,修改好配置重启服务,然后用 info replication 检查从节点状态:
bash
redis-cli INFO replication
返回信息中,role 应为 slave,master_link_status 应为 up,表示从节点正在成功连接到主节点。
后续可以主库写入数据、从库查看来检验主从复制效果,这里不展开。
到这里 Redis 基本的主从复制已经完成,但是有几个问题:
- 如果主节点挂了怎么办,怎么完成业务切换与故障转移?
- 怎么保证 Redis 的横向拓展性、实时性、高可用性?
- 默认情况下,Redis 主从复制不会持久化从节点的数据,怎么实现持久化?
后续的 Redis 哨兵部署、Cluster 集群、Redis 持久化会解答这些问题。