Back
详情
Redis——详解主从模式的原理和优缺点,介绍主从复制的详细流程

什么是主从模式

主从模式通常是一台主机负责写入服务,多台备机只负责提供读服务。这个模式可以保证多台服务器的数据一致性,且主从服务器之间采用的是「读写分离」的方式。

主服务器可以进行读写操作,当发生写操作时自动将写操作同步给从服务器,而从服务器一般是只读,并接受主服务器同步过来写操作命令,然后执行这条命令

主从模式下最主要的是如何实现主备机的数据一致性,接下来就要介绍下主从同步的过程:

主从同步/复制机制

每次当 slave 和 master 之间的连接断开时, slave 会自动重连到 master 上,并且无论这期间 master 发生了什么, slave 都将尝试让自身成为 master 的精确副本。

这个系统的运行依靠三个主要的机制:

  • 当一个 master 实例和一个 slave 实例连接正常时, master 会发送一连串的命令流来保持对 slave 的更新,以便于将自身数据集的改变复制给 slave , :包括客户端的写入、key 的过期或被逐出等等。
  • 当 master 和 slave 之间的连接断开之后,因为网络问题、或者是主从意识到连接超时, slave 重新连接上 master 并会尝试进行部分重同步:这意味着它会尝试只获取在断开连接期间内丢失的命令流
  • 当无法进行部分重同步时, slave 会请求进行全量重同步。这会涉及到一个更复杂的过程,例如 master 需要创建所有数据的快照,将之发送给 slave ,之后在数据集更改时持续发送命令流到 slave 。

Redis使用默认的异步复制,其特点是低延迟和高性能,是绝大多数 Redis 用例的自然复制模式。但是,从 Redis 服务器会异步地确认其从主 Redis 服务器周期接收到的数据量。

客户端可以使用** WAIT 命令**来请求同步复制某些特定的数据。但是,WAIT 命令只能确保在其他 Redis 实例中有指定数量的已确认的副本:在故障转移期间,由于不同原因的故障转移或是由于 Redis 持久性的实际配置,故障转移期间确认的写入操作可能仍然会丢失

注意点:

  • Redis 使用异步复制,slave 和 master 之间异步地确认处理的数据量
  • 一个 master 可以拥有多个 slave
  • slave 可以接受其他 slave 的连接。除了多个 slave 可以连接到同一个 master 之外, slave 之间也可以像层叠状的结构(cascading-like structure)连接到其他 slave 。自 Redis 4.0 起,所有的 sub-slave 将会从 master 收到完全一样的复制流。
  • Redis 复制在 master 侧是非阻塞的。这意味着 master 在一个或多个 slave 进行初次同步或者是部分重同步时,可以继续处理查询请求。
  • 复制在 slave 侧大部分也是非阻塞的当 slave 进行初次同步时,它可以使用旧数据集处理查询请求,假设你在 redis.conf 中配置了让 Redis 这样做的话。否则,你可以配置如果复制流断开, Redis slave 会返回一个 error 给客户端。但是,在初次同步之后,旧数据集必须被删除,同时加载新的数据集。 slave 在这个短暂的时间窗口内(如果数据集很大,会持续较长时间),会阻塞到来的连接请求。自 Redis 4.0 开始,可以配置 Redis 使删除旧数据集的操作在另一个不同的线程中进行,但是,加载新数据集的操作依然需要在主线程中进行并且会阻塞 slave 。
  • 复制既可以被用在可伸缩性,以便只读查询可以有多个 slave 进行(例如 O(N) 复杂度的慢操作可以被下放到 slave ),或者仅用于数据安全。
  • 可以使用复制来避免 master 将全部数据集写入磁盘造成的开销:一种典型的技术是配置你的 master Redis.conf 以避免对磁盘进行持久化,然后连接一个 slave ,其配置为不定期保存或是启用 AOF。但是,这个设置必须小心处理,因为重新启动的 master 程序将从一个空数据集开始:如果一个 slave 试图与它同步,那么这个 slave 也会被清空

当 master 关闭持久化时,复制的安全性

在使用 Redis 复制功能时的设置中,强烈建议在 master 和在 slave 中启用持久化当不可能启用时,例如由于非常慢的磁盘性能而导致的延迟问题,应该配置实例来避免重置后自动重启

为了更好地理解为什么关闭了持久化并配置了自动重启的 master 是危险的,检查以下故障模式,这些故障模式中数据会从 master 和所有 slave 中被删除

  1. 我们设置节点 A 为 master 并关闭它的持久化设置,节点 B 和 C 从 节点 A 复制数据。
  2. 节点 A 崩溃,但是他有一些自动重启的系统可以重启进程。但是由于持久化被关闭了,节点重启后其数据集合为空。
  3. 节点 B 和 节点 C 会从节点 A 复制数据,但是节点 A 的数据集是空的,因此复制的结果是它们会销毁自身之前的数据副本

当 Redis Sentinel 被用于高可用并且 master 关闭持久化,这时如果允许自动重启进程也是很危险的。例如, master 可以重启的足够快以致于 Sentinel 没有探测到故障,因此上述的故障模式也会发生。

任何时候数据安全性都是很重要的,所以如果 master 使用复制功能的同时未配置持久化,那么自动重启进程这项应该被禁用

Redis 复制功能的工作流程

首先介绍两个概念:Replication ID, offset

Replication ID(或runId)和offset

  • 每一个 Redis master 都有一个 replication ID(runId) :这是一个较大的伪随机字符串,标记了一个给定的数据集
  • 每个 master 也持有一个偏移量,master 将自己产生的复制流发送给 slave 时,发送多少个字节的数据,自身的偏移量就会增加多少,目的是当有新的操作修改自己的数据集时,它可以以此更新 slave 的状态

复制偏移量即使在没有一个 slave 连接到 master 时,也会自增。
因此一对 (Replication ID, offset) 就唯一标识一个 master 数据集的确切版本

下面仔细说一下同步过程

建立连接

Slave
执行
replicaof
ip port,准备与
Master 节点建立复制连接。连接建立后,Slave
节点将会向
Master 节点发送
psync
?
-1 命令,该命令包含了主节点的
runID 以及从节点的复制偏移量
offset。

psync中的 ?为replicatioin id(runID),但由于是初次连接所以为?;
这里的-1 位offset,但由于是初次连接还未进行任何复制,所以用-1。

生成并发送RDB

主节点执行
bgsave 命令生成
RDB 文件并发送给从节点,并且为每一个从节点开辟一个
Replication
Buffer 缓冲区,用于记录从生成
RDB 文件开始所接收到的所有写入命令。

主意这里是每一个节点对应一个Replication
Buffer 缓冲区

#### Replication
Buffer

replication buffer里存的是从生成
RDB 文件开始所接收到的所有写入命令。

里面主要存放以下时机的数据:

  • Master 在执行 bgsave 期间产生的写入数据
  • Master 发送
RDB 文件到
Slave
期间的写入数据
  • Slave
将
RDB
Load 到内存期间的写入数据

注意:

如果
Replication
Buffer 已满,那么
Master 会断开与
Slave
的连接,重新开始全量复制。所以,如果
Replication
Buffer 设置的太小的话将会导致恶性循环。

增量复制

在Master将rdb发送给Slave,且Slave将rdb加载进内存后,Master 将
Replication
Buffer 的数据持续地发送给
Slave
节点,此时为增量的数据。

Redis的增量复制

断线处理

当从节点掉线以后,Master 中该
Slave
的
Replication
Buffer 将会被释放,那么
Slave
重连以后会进行全量同步吗?从
Redis
2.8 开始,网络断了之后,主从库会采用增量复制的方式继续同步,Redis 使用
Replication Backlog
Buffer 实现增量复制

Replication-Backlog-Buffer结构

Replication
Backlog
Buffer 是一个环形数组,其中记录
Master 节点在一段时间以内的全部写入操作。当
buffer 写满了以后,会从头开始覆盖前面的内容,并继续写入。image.pngimage.png

上线后如何再增量复制

当
Slave 重新上线以后,向
Master 节点发送
psync
{runID}
{offset},此时
offset 就不再是
-1 了,而是
Slave 实际的复制偏移量

  • 如果当前
Slave 的
slave repl
position 还没有被覆盖的话,那么
Master 只需要把
Slave 落后的那一部分数据通过
Replication
Buffer 发送给
Slave 即可。
  • 如果已经被覆盖,那么此时只能进行全量同步

qa

假如Slave重启了,是否会进行全量复制?

Redis 会持久化
runID 和
offset 这两个关键信息,重启后直接载入,并向
Master 节点发送
psync
{runID}
{offset}。
若此时
Master 中
backlog
buffer 中
offset 的位置还没有被覆盖的话,那么可以进行增量同步。否则,仍然进行全量同步

Slave节点对于设置了TTL的key会主动删除吗?

Slave
不会主动删除过期
key,而是等着
Master 删除后,将
DELETE 命令发送给
Slave

优点

  1. 配置简单,易于实现。
  2. 实现数据冗余,提高数据可靠性。
  3. 读写分离,提高系统性能

缺点

  1. 主节点故障时,需要手动切换到从节点,故障恢复时间较长。
  2. 主节点承担所有写操作,可能成为性能瓶颈。
  3. 无法实现数据分片,受单节点内存限制。

使用场景

主从复制模式适合数据备份、读写分离和在线升级等场景,但在主节点故障时需要手动切换,不能自动实现故障转移。如果对高可用性要求较高,可以考虑使用哨兵模式或Cluster模式。