目录
- [1. 主从复制详解](#1. 主从复制详解)
- [1.1 配置篇 - 实操](#1.1 配置篇 - 实操)
- [1.1.1 建立复制](#1.1.1 建立复制)
- [1.1.2 断开复制](#1.1.2 断开复制)
- [1.1.3 安全性](#1.1.3 安全性)
- [1.1.4 只读](#1.1.4 只读)
- [1.1.5 传输延迟](#1.1.5 传输延迟)
- [1.2 原理篇](#1.2 原理篇)
- [1.2.1 建立复制的完整过程](#1.2.1 建立复制的完整过程)
- [1.2.2 数据同步 psync](#1.2.2 数据同步 psync)
- [1.1 配置篇 - 实操](#1.1 配置篇 - 实操)
- [2. 小结](#2. 小结)
- [3. 经典面试题](#3. 经典面试题)
✅️ 1. 主从复制详解
❓️ 问题导入
如果某个服务器程序,只有一个节点(只搞一个物理服务器,来部署这个服务器程序),此时就会出现以下两个问题:
1.可用性问题,如果这个机器挂了,意味着服务就会中断
2.性能和支持的并发量比较有限。
为了在分布式系统中解决上述的这种"单点问题",通常会减法数据复制多个副本然后部署到其他服务器,实现故障恢复和负载均衡,Redis也有同样的策略来解决单点问题。
1.1 配置篇 - 实操
1.1.1 建立复制
参与主从复制的Redis示例可以分为主节点和从节点,两者呈现一对多的关系,即主节点在集群中只能有一个,从节点可以有多个,且复制的数据流向是单向的,只能从主节点流向从节点。配置主从节点有以下三种方式:
① 在一个从节点Redis示例配置文件中加入 slaveof {主节点Host} {主节点Redis实例端口号},启动即生效。
② 在redis-server启动命令时加入--slaveof {masterHost} {masterPort}生效。
③ 直接使用命令:`slaveof {masterHost} {masterPort} 生效
配置过程:
- 复制一份
redis的配置文件redis.conf,将其中的daemonize属性修改为yes,然后命名为redis-slave.conf文件,这一步操作是为了将后续的从节点实例都变为后台进程。 - 启动默认的
redis作为主Redis,然后重新执行以下命令作为从节点实例。
bash
ubuutu
redis-server /etc/redis/redis-slave.conf --port 6380 --slaveof 127.0.0.1 6379
centos
redis-server /etc/redis-slave.conf --port 6380 --slaveof 127.0.0.1 6379
📚 注意:修改配置主要是修改从机的配置,主机的配置不变。
bash
# 使用netstat -nltp 查看启动的实例
# 从节点实例
[root@localhost lijing]# redis-server ./redis-slave.conf --port 6380 --slaveof 127.0.0.1 6379
[root@localhost lijing]# netstat -nltp | grep 6380
tcp 0 0 127.0.0.1:6380 0.0.0.0:* LISTEN 15071/redis-server
# 主节点实例
[root@localhost lijing]# netstat -nltp | grep 6379
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 1007/redis-server 1
此时可以通过redis-cli来连接主Redis示例,也可以通过命令redis-cli -p 6380 连接从redis.
✏️ 注意:注意:
kill -9 pid:这种停止redis-server的方式,是和通过直接运行redis-server命令的方式搭配的。而如果是使用service redis-server start这种方式启动的,就必须使用service redis-server stop来停止。
完成上述主从节点的配置之后,可以通过info replication 命令来查看复制相关状态。
bash
# 主节点信息
[root@localhost lijing]# redis-cli
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=210,lag=1
master_replid:c16a8b270c358ba368c89cbbc662ccce1dc03e62
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:210
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:210
# 从节点信息
[root@localhost lijing]# redis-cli -p 6380
127.0.0.1:6380> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:9
master_sync_in_progress:0
slave_repl_offset:238
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:c16a8b270c358ba368c89cbbc662ccce1dc03e62
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:238
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:238
✏️ 理解
slave0:ip=127.0.0.1,port=6380,state=online,offset=210,lag=1中的offset参数表示主从节点之间同步数据的进度。- 最后三行参数表示积压缓冲区,支持部分同步机制的实现。
- 主节点示例会收到客户端源源不断的"修改数据"的请求,从节点就需要从主节点这里同步这些修改请求。从节点和主节点之间的数据同步不是瞬间完成的。
1.1.2 断开复制
slaveof 命令不但可以建⽴复制,还可以在从节点 执⾏slaveof no one来断开与主节点复制关系。
例如在 6380 节点上执⾏ slaveof no one 来断开复制。
断开复制主要流程 :
1)断开与主节点复制关系。
2)从节点晋升为主节点。
从节点断开复制后并不会抛弃原有数据,只是⽆法再获取主节点上的数据变化。
通过 slaveof 命令还可以实现切主操作,将当前从节点的数据源切换到另⼀个主节点。执⾏slaveof {newMasterIp} {newMasterPort} 命令即可。
切主操作主要流程 :
1)断开与旧主节点复制关系。
2)与新主节点建⽴复制关系。
3)删除从节点当前所有数据。
4)从新主节点进⾏复制操作。
1.1.3 安全性
对于数据⽐较重要的节点,主节点会通过设置requirepass参数进⾏密码验证,这时所有的客⼾端访问必须使⽤auth命令实⾏校验。从节点与主节点的复制连接是通过⼀个特殊标识的客⼾端来完成,因此需要配置从节点的masterauth 参数与主节点密码保持⼀致,这样从节点才可以正确地连接到主节点并发起复制流程。
1.1.4 只读
默认情况下,从节点使⽤slave-read-only=yes配置为只读模式。由于复制只能从主节点到从节点,对于从节点的任何修改主节点都⽆法感知,修改从节点会造成主从数据不⼀致。所以建议线上不要修改从节点的只读模式。
1.1.5 传输延迟
主从节点⼀般部署在不同机器上,复制时的⽹络延迟就成为需要考虑的问题,Redis 为我们提供了repl-disable-tcp-nodelay参数⽤于控制是否关闭 TCP_NODELAY,默认为 no,即开启tcp-nodelay功能,说明如下:
- 当关闭时,主节点产⽣的命令数据⽆论⼤⼩都会及时地发送给从节点,这样主从之间延迟会变⼩,但增加了⽹络带宽的消耗。适⽤于主从之间的⽹络环境良好的场景,如同机房部署。
- 当开启时,主节点会合并较⼩的
TCP数据包从⽽节省带宽。默认发送时间间隔取决于Linux的内核,⼀般默认为40毫秒。这种配置节省了带宽但增⼤主从之间的延迟。适⽤于主从⽹络环境复杂的场景,如跨机房部署。
📚 理解
主节点和从节点之间通过网络来传输
(TCP),TCP内部支持了nagle算法(默认是开启的)开启了,就会增加
tcp的传输延迟,节省网络带宽关闭了,就会减少
tcp的传输延迟,增加了网络带宽目的和
tcp的捎带应答机制一样,针对小的tcp数据报,进行合并,减少了包的个数
1.2 原理篇
1.2.1 建立复制的的完整过程

1.2.2 数据同步psync
Redis使用psync命令来实现主从数据同步。同步过程又分为:全量复制和部分复制
- 全量复制:一般用于初始复制场景,Redis早期支持的复制功能只有全量复制,他会把主节点全部数据一次性发送给从节点,当数据量过大时,会对主从接地那和网络造成很大的开销。
- 部分复制:用于处理主从复制中因为网络闪断等问题造成的数据丢失场景,当从节点再次连上主节点后,如果条件允许,主节点会补发数据给从节点。因为补发的数据很小,可以有效避免全量复制的高开销。
语法 :PSYNC replicationid offset
说明 :
如果replicationid设置为 ? ,且offset设置为-1表示尝试进行全量复制。
如果replicationid offset 设置了具体的数值,则是尝试进行部分复制。
📌 理解
psync命令不需要我们手动执行,redis服务器会在建立好主从复制关系后,制动执行psync.
① replicationid / replid (复制id)
主节点的复制id,主节点重新启动或者从节点变为主节点,都会生成一个全新的replicationid.从节点和主节点建立好复制关系后,会自动获取主节点的replicationid.
bash
# 使用命令info replication 可以查看主节点的replid
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:0
master_replid:726e868a37583fee7a064e4b2b94bfc955834327
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
🐻
master_replid和master_replid2参数解析每个节点需要记录两组
master_replid. 这个设定解决的问题场景是这样的:⽐如当前有两个节点
A和B,A为master,B为slave.此时
B就会记录A的master_replid.如果⽹络出现抖动,
B以为A挂了,B⾃⼰就会成为主节点. 于是B给⾃⼰分配了新的master_replid.此时就会使⽤
master_replid2来保存之前A的master_replid.• 后续如果⽹络恢复了,
B就可以根据master_replid2找回之前的主节点.• 后续如果⽹络没有恢复,
B就按照新的master_replid⾃成⼀派, 继续处理后续的数据
② offset(偏移量)
参与复制的主从节点都会维护⾃⾝复制偏移量。主节点(master)在处理完写⼊命令后,会把命令的字节⻓度做累加记录,统计信息在info replication中的 master_repl_offset 指标中。
bash
127.0.0.1:6379> info replication
# Replication
role:master
...
master_repl_offset:1055130
从节点(slave)每秒钟上报⾃⾝的复制偏移量给主节点,因此主节点也会保存从节点的复制偏移量,统计指标如下:
bash
127.0.0.1:6379> info replication
connected_slaves:1
slave0:ip=127.0.0.1,port=6380,state=online,offset=1055214,lag=1
...
从节点在接受到主节点发送的命令后,也会累加记录⾃⾝的偏移量。统计信息在info replication中的slave_repl_offset 指标中:
bash
127.0.0.1:6380> info replication
# Replication
role:slave
...
slave_repl_offset:1055214
📌 结论
replid + offset共同标识了⼀个 "数据集".如果主从两个节点, 他们的replid和offset都相同, 则这两个节点上持有的数据, 就⼀定相同.
下图是psync的具体运行流程

全量复制全流程

🐳 有磁盘复制 vs ⽆磁盘复制(diskless)
默认情况下, 进⾏全量复制需要主节点⽣成
RDB⽂件到主节点的磁盘中, 再把磁盘上的RDB⽂件通过发送给从节点.Redis 从 2.8.18 版本开始⽀持⽆磁盘复制. 主节点在执⾏
RDB⽣成流程时, 不会⽣成RDB⽂件到磁盘中了, ⽽是直接把⽣成的RDB数据通过⽹络发送给从节点. 这样就节省了⼀系列的写硬盘和读硬盘的操作开销.即使引入了无磁盘模式,仍然整个操作是比较重量的,比较耗时的,是没法省的相比较于网络传输,读写硬盘是小头。
部分复制
部分复制主要是 Redis 针对全量复制的过⾼开销做出的⼀种优化措施,使⽤psync replicationId offset命令实现。当从节点正在复制主节点时,如果出现**⽹络闪断或者命令丢失** 等异常情况时,从节点会向主节点要求补发丢失的命令数据,如果主节点的复制积压缓冲区存在数据则直接发送给从节点,这样就可以保持主从节点复制的⼀致性。
部分复制全流程

复制积压缓冲区
复制积压缓冲区是保存在主节点上的⼀个固定⻓度的队列,默认⼤⼩为 1MB,当 主节点有连接的从节点(slave)时 被创建,这时主节点(master)响应写命令时,不但会把命令发送给从节点,还会写⼊复制积压缓冲区.由于缓冲区本质上是先进先出的定⻓队列,所以能实现保存最近已复制数据的功能,⽤于部分复制和
复制命令丢失的数据补救。复制缓冲区相关统计信息可以通过主节点的 info replication 中:
bash
127.0.0.1:6379> info replication
# Replication
role:master
...
repl_backlog_active:1 // 开启复制缓冲区
repl_backlog_size:1048576 // 缓冲区最大长度
repl_backlog_first_byte_offset:7479 // 起始偏移量,计算当前缓冲区可用范围
repl_backlog_histlen:1048576 // 已保存数据的有效长度
复制积压缓冲区内的可⽤偏移量范围为:[repl_backlog_first_byte_offset,repl_backlog_first_byte_offset + repl_backlog_histlen]。
📌 理解
这个相当于⼀个基于数组实现的环形队列. 上述区间中的值就是 "数组下标" .
如果当前从节点需要的数据, 已经超出了主节点的积压缓冲区的范围, 则⽆法进⾏部分复制, 只能全量复制了.
实时复制

🔥拓展
从节点和主节点之间断开连接,有两种情况:
- 从节点自动和主节点断开连接 (此时,从节点能够晋升为主节点)
- 主节点挂了,这个时候从节点不会晋升成为主节点,必须通过人工干预的方式恢复主节点
❓️ 拓展问题:Redis主节点掉线后,普通用户无法重新启动的问题
- 问题原因
在Redis主从复制环境中,如果之前使用root用户启动Redis,那么Redis生成的AOF持久化文件及相关目录可能属于root用户。
当主节点掉线后,再使用普通用户启动Redis时,由于文件或目录权限不足,可能无法正常读取或写入AOF文件,导致 Redis 启动失败。
注意:不推荐直接使用root用户启动Redis。root权限过高,一旦Redis存在安全漏洞或遭到攻击,可能带来更大的安全风险。 - 解决方案
推荐做法:为不同Redis实例配置独立的工作目录,并确保目录权限正确。
如果多个Redis实例共用同一个工作目录,可能出现持久化文件冲突、文件所有权不一致等问题。
可以通过修改Redis配置文件中的dir选项,为每个实例指定独立的工作目录。
具体操作如下。
第一步:停止之前运行的Redis实例
先停止相关Redis服务,避免修改文件和目录时影响正在运行的实例。
第二步:处理原有AOF文件的权限
如果AOF文件属于root用户,可以通过chown命令修改文件所有者:
bash
sudo chown redis:redis appendonly.aof
其中,redis:redis 分别表示文件所属用户和用户组。
需要注意,如果是目录权限问题,还需要调整相应目录的所有权和访问权限。
如果旧 AOF 数据已经不需要,也可以在确认数据可丢弃并完成必要备份后删除旧文件。
不建议直接删除仍然需要恢复数据的 AOF 文件,否则可能导致数据丢失。
第三步:为主从节点创建独立的工作目录
例如,在同一台服务器上运行两个 Redis 实例,可以创建:
bash
sudo mkdir -p /var/lib/redis/master
sudo mkdir -p /var/lib/redis/replica
然后设置目录所有权:
bash
sudo chown -R redis:redis /var/lib/redis/master
sudo chown -R redis:redis /var/lib/redis/replica
第四步:修改主从节点的 Redis 配置文件
主节点配置:
bash
dir /var/lib/redis/master
从节点配置:
bash
dir /var/lib/redis/replica
这样主从节点就拥有了各自独立的工作目录,不会因为共用工作目录而发生持久化文件冲突。
第五步:使用普通用户重新启动 Redis
确认工作目录及持久化文件权限正确后,再使用指定的普通用户启动 Redis。
✅️ 2. 小结
本节围绕 Redis 主从复制,从「配置实操」和「原理机制」两条主线展开,核心要点可以归纳为下面几条:
- 主从复制的定位 :主从复制是
Redis解决单点问题的第一层手段,主节点唯一、从节点可多个,数据单向从主节点流向从节点,用于故障恢复与读请求的负载均衡。 - 三种建立方式 :配置文件写入
slaveof {masterHost} {masterPort}、启动命令追加--slaveof、运行时执行slaveof命令,三者效果一致,区别只在生效时机。 - 常用运维命令 :
info replication查看复制状态;slaveof no one断开复制并让从节点晋升为主节点;slaveof {newMasterIp} {newMasterPort}完成切主(会先删除从节点当前所有数据)。 - 安全与只读 :主节点设置
requirepass后,从节点必须配置一致的masterauth;从节点默认slave-read-only=yes,线上不建议关闭,否则会造成主从数据不一致。 - 传输延迟 :
repl-disable-tcp-nodelay控制是否关闭TCP_NODELAY,本质是TCP的nagle算法在「低延迟」与「省带宽」之间做取舍,同机房建议关闭、跨机房建议开启。 - 同步机制 :
psync命令承载同步,分为全量复制与部分复制;replid + offset共同标识一个数据集,两者都相同则数据一定相同。 - 全量复制 :主节点生成
RDB发送给从节点,开销大;2.8.18 起支持无磁盘复制(diskless),省去读写磁盘,但整体依然是重量级操作。 - 部分复制 :依赖主节点上的复制积压缓冲区(默认
1MB的定长环形队列),网络闪断或命令丢失后补发增量数据;若所需数据已超出缓冲区范围,则只能退化为全量复制。 - 实时复制:建立复制关系后,主节点持续把写命令异步发送给从节点,保证数据最终一致。
一句话总结:主从复制 = 一次全量复制打底 + 长期实时复制维持 + 断连时用部分复制补救 ,而 replid 与 offset 是判断「能不能只补一部分」的关键依据。
✅️ 3. 经典面试题
Q1:Redis 主从复制的作用是什么?能解决什么问题?
主从复制主要有三个作用:一是数据冗余与故障恢复 ,主节点故障后可以从从节点恢复数据;二是读扩展 ,把读请求分摊到多个从节点,提升整体并发能力;三是读写分离的基础 ,为后续哨兵、集群等高可用方案提供支撑。需要注意的是,主从复制本身不解决主节点自动故障转移,那需要哨兵或集群来完成。
Q2:主从复制的建立方式有哪几种?
三种:① 在从节点配置文件中写入 slaveof {masterHost} {masterPort},启动即生效;② 启动 redis-server 时通过 --slaveof {masterHost} {masterPort} 指定;③ 运行时在从节点执行 slaveof {masterHost} {masterPort} 命令,立即生效。前两种适合固化配置,第三种适合临时调整或切主。
Q3:slaveof no one 和 slaveof {newMasterIp} {newMasterPort} 有什么区别?
slaveof no one 是断开复制 :从节点断开与主节点的关系并晋升为主节点,原有数据保留,只是不再接收主节点的数据变化。slaveof {newMasterIp} {newMasterPort} 是切主 :先断开旧主节点,再与新主节点建立复制关系,并且会删除从节点当前所有数据,然后从新主节点重新做一次复制。前者是「独立」,后者是「换源」,切主是有数据丢失风险的,务必谨慎。
Q4:主节点设置了密码,从节点为什么连不上?怎么解决?
主节点通过 requirepass 开启密码校验后,从节点与主节点的复制连接本质上也是一个特殊标识的客户端,同样需要通过校验。因此从节点必须配置 masterauth,且与主节点的 requirepass 保持一致,否则复制流程无法建立。
Q5:从节点为什么建议保持只读?
因为复制是单向的,主节点无法感知从节点的写入。如果允许从节点写入,这部分数据不会被同步到主节点,也不会同步给其他从节点,直接导致主从数据不一致。所以默认 slave-read-only=yes,线上不建议关闭。
Q6:repl-disable-tcp-nodelay 这个参数怎么理解?
它控制是否关闭 TCP_NODELAY,默认 no,即开启 tcp-nodelay 功能。关闭 TCP_NODELAY(即参数为 no)时,命令数据无论大小都及时发送,延迟小但更耗带宽,适合同机房部署;开启 TCP_NODELAY(即参数为 yes)时,主节点会合并较小的 TCP 数据包,默认约 40 毫秒发送一次,省带宽但延迟增大,适合跨机房部署。其本质与 TCP 的 nagle 算法、捎带应答机制一致,都是对小包做合并。
Q7:全量复制和部分复制的区别是什么?
全量复制一般用于初始复制 场景,主节点把全部数据一次性发送给从节点,数据量大时对主从节点和网络开销都很大。部分复制用于处理网络闪断、命令丢失 等场景,从节点重连后,若条件允许,主节点只补发丢失的那部分数据,开销很小。Redis 早期只有全量复制,部分复制是后来针对全量复制高开销做的优化。
Q8:psync 命令的语法是什么?参数含义如何?
语法为 PSYNC replicationid offset。当 replicationid 为 ? 且 offset 为 -1 时,表示尝试进行全量复制 ;当两者都是具体数值时,表示尝试进行部分复制 。该命令不需要手动执行,Redis 在建立好主从关系后会自动发起。
Q9:replid 和 offset 分别是什么?为什么说它们能标识一个数据集?
replid 是主节点的复制 ID,主节点重启或从节点晋升为主节点时都会生成全新的 replid,从节点建立关系后会自动获取主节点的 replid。offset 是复制偏移量,主节点每处理完一条写命令就累加命令的字节长度(master_repl_offset),从节点接收命令后也累加自身偏移量(slave_repl_offset),并每秒上报给主节点。replid + offset 共同标识一个数据集 :如果两个节点的 replid 和 offset 都相同,那么它们持有的数据一定相同。
Q10:master_replid2 是干什么用的?
每个节点会记录两组 master_replid。假设 A 为主、B 为从,B 记录 A 的 master_replid;若网络抖动导致 B 误以为 A 挂了而自立为主,B 会给自己分配新的 master_replid,同时用 master_replid2 保存原来 A 的 master_replid。这样网络恢复后,B 可以依据 master_replid2 找回原来的主节点;若网络未恢复,B 就按新的 master_replid 自成体系继续处理数据。
Q11:什么是复制积压缓冲区?它和部分复制是什么关系?
复制积压缓冲区是保存在主节点上的一个固定长度的环形队列 ,默认 1MB,在有从节点连接时创建。主节点响应写命令时,除了发给从节点,还会写入该缓冲区,从而保存最近已复制的数据。部分复制正是依赖它:从节点重连后请求补发,若所需数据仍在缓冲区范围内([repl_backlog_first_byte_offset, repl_backlog_first_byte_offset + repl_backlog_histlen]),主节点直接补发;若已超出范围,则只能退化为全量复制。
Q12:什么是无磁盘复制(diskless)?它能完全消除全量复制的开销吗?
默认全量复制需要主节点先把 RDB 落到磁盘,再读出来发送给从节点。Redis 从 2.8.18 起支持无磁盘复制:主节点生成 RDB 时不再写磁盘,而是直接把数据通过网络发送给从节点,省去了写盘和读盘的开销。但它并不能消除全量复制的整体开销------相比网络传输,读写硬盘只是小头,整个全量复制依然是重量级、耗时的操作。
Q13:主节点掉线后,从节点会自动晋升为主节点吗?
不会。主从复制本身只负责数据同步,不负责故障转移。从节点主动断开(如执行 slaveof no one)时可以晋升为主节点;但如果是主节点挂了,从节点不会自动晋升,必须通过人工干预或引入哨兵(Sentinel)/集群(Cluster)机制来完成故障转移。
Q14:为什么用 root 启动过 Redis 后,普通用户再启动会失败?
因为 root 启动时生成的 AOF 持久化文件及相关目录归属 root,普通用户权限不足,无法读写这些文件,导致启动失败。解决方案是为不同实例配置独立工作目录(修改 dir 配置),并用 chown 把文件和目录所有权改为运行用户(如 redis:redis)。同时也不推荐用 root 启动 Redis,权限过高会放大安全风险。
Q15:如果从节点需要的数据已经不在积压缓冲区里了,会发生什么?
无法进行部分复制,只能退化为全量复制 。这也是为什么缓冲区大小需要根据实际写入量和可能的断连时长来评估:repl_backlog_size 过小,网络稍有波动就可能触发全量复制,给主从节点和网络带来较大压力。