Jamzy Wang

life is a struggle,be willing to do,be happy to bear~~~

Redis主从复制详解

2015-02-17 21:49

原创声明:本作品采用知识共享署名-非商业性使用 3.0 版本许可协议进行许可,欢迎转载,演绎,但是必须保留本文的署名(包含链接),且不得用于商业目的。

redis 支持简单且易用的主从复制(master-slave replication)功能, 该功能可以让从服务器(slave server)成为主服务器(master server)的精确复制品。 以下是关于 Redis 复制功能的几个重要方面:

  • redis 使用异步复制。 异步复制就意味着master和slave之间会存在一致性问题,本文后半部分将详解redis是如何处理一致性问题的。

  • redis支持多级主从(Chained Replication)同步。一个master可以有多个slave,salve也可以有自己的slave。

此处输入图片的描述

  • 复制功能不会阻塞master也不会阻塞slave。即使有一个或多个从服务器正在进行初次同步, 主服务器也可以继续处理命令请求。

  • 复制功能可以单纯地用于数据冗余(当master宕机时,slave可以接替master成为新的master),也可以通过让多个从服务器处理只读命令请求来提升扩展性(如繁重的 SORT 命令可以交给salve节点去执行)。

  • 可以通过复制功能来让备份数据而不需要主服务器执行持久化操作(只要关闭主服务器的持久化功能, 然后由从服务器去执行持久化操作即可)。

主从复制实现

1. 初次主从复制

redis replication初次连接时主要执行下述6个步骤:

(1) 从服务器向主服务器发送 SYNC 命令。(redis 2.8 开始可以选择使用PSYNC或者SYNC

(2) 主服务器调用 BGSAVE 命令 ,创建一个.rdb 文件,并使用缓冲区记录接下来执行的所有写命令

(3) 主服务器向从服务器发送 .rdb 文件

(4) 从服务器接收.rdb文件并载入该文件

(5) 主服务器将缓冲区储存的所有写命令发送给从服务器

(6) 从服务器执行接收到的所有写命令

此处输入图片的描述

无论是初次连接还是重新连接, 当建立一个从服务器时,从服务器都将向主服务器发送一个 SYNC 命令。接到 SYNC 命令的主服务器将开始执行 BGSAVE 命令 , 并在 BGSAVE 执行期间,将所有新的主服务器的写入命令都保存到一个缓冲区里面。当 BGSAVE 命令执行完毕后,主服务器将执行所得的 .rdb 文件发送给从服务器, 从服务器接收这个 .rdb 文件,并将文件中的数据载入到内存中。之后主服务器会以 redis 命令协议的格式, 将写命令缓冲区中积累的所有写命令发送给从服务器。

SYNC 命令执行示例 : 此处输入图片的描述

当有多个从服务器同时向主服务器发送 SYNC , 主服务器也只需执行一次 BGSAVE 命令,就可以处理所有这些从服务器的同步请求。

在redis 2.8 版本之前,当从服务器断开连接后又重新连接上时,主从服务器之间会重新执行上述6个步骤进行主从同步,也就是执行一次完整重同步(full resynchronization)操作,主服务器发送完整的拷贝给从服务器。但是在这段掉线的时间里,主服务器可能只执行了有限次的写操作,没有必要发送主服务器的完整拷贝。从 redis 2.8 版本开始, 从服务器可以根据主服务器的情况来选择执行完整重同步还是部分重同步(partial resynchronization)。

从 redis 2.8 开始, 在网络连接短暂性失效之后, 从服务器会重新连接, 并且向主服务器请求继续执行原来的复制进程:

  • 如果从服务器记录的主服务器 ID 和当前要连接的主服务器的 ID 相同, 那么只需同步未同步的数据。
  • 否则的话, 从服务器就要执行完整重同步操作。

redis 2.8 的这个部分重同步特性会用到一个新增的 PSYNC 内部命令:

1
2
如果主服务器是 redis 2.8 或以上版本,那么从服务器使用 PSYNC 命令来进行同步。
如果主服务器是 redis 2.8 之前的版本,那么从服务器使用 SYNC 命令来进行同步。

此处输入图片的描述

如果主从服务器所处的网络环境并不是特别稳定的话(经常断线),推荐使用 redis 2.8 或以上版本,这可以节约大量网络资源、计算资源、内存资源 —— 这些消耗都来自重复创建和传输 RDB文件。

2. 后续主从复制

执行 SYNC 命令之后,主从服务器的数据库状态将达到一致状态(两个数据库都储存了相同的数据),但这种一致性只是暂时性的,因为一旦主服务器执行了新的写命令,主从服务器的数据库又会变得不一致。那么如何保持主从服务器的同步呢?

在主从服务器完成同步之后,主服务器每执行一个写命令,它都会将被执行的写命令发送给从服务器执行,这个操作被称为“命令传播”(command propagate),命令传播是一个一直持续的操作。

命令传播需要花费一段时间来完成,如果master和slave同时对外提供服务,因redis使用异步复制同步主从数据,那么在命令传播执行这段时间内会发生主从状态的不一致。考虑这样一种情况:

此处输入图片的描述

客户端与主服务器的连接非常快,而主从服务器之间的连接却非常慢。客户端向主服务器发送了命令 SET 10086,并在获得返回 OK 之后,向从服务器发送 GET n 。但是这时主服务器传播的 SET n 10086 因为网络原因仍然未到达从服务器,那么客户端获得的键 n 的值将是错误的(过期的)。

由于目前为止redis只提供异步复制方式,因此只能保证数据的最终一致性,不能保证强一致性,在需要强一致性的应用场景下应该关闭从服务器的读操作。

主从配置

redis的主从配置非常简单,只需要在slave的配置文件中添加如下一行配置:

1
slaveof 192.168.1.1 6379

其中192.168.1.16379分别是master的IP和端口号。

另外一种方法是调用 SLAVEOF 命令, 输入主服务器的 IP 和端口, 然后同步就会开始:

1
127.0.0.1:6379> SLAVEOF 192.168.1.1 10086

Failover方案

redis主从复制的一个最大应用场景就是用来做failover,当master发生故障时,slave成为matser提供对外的服务。redis中用一个sentinel工具来具体执行failover的工作,从一堆slave中选择一个成为master,剩余的slave成为这个新的mater的slave。关于sentinel的原理将在另一篇文章中具体阐释。failover大致过程如下:

此处输入图片的描述

此处输入图片的描述

此处输入图片的描述

此处输入图片的描述

redis的两个特性

只读从服务器

从 Redis 2.6 开始, 从服务器支持只读模式, 并且该模式为从服务器的默认模式。 只读模式由 redis.conf 文件中的 slave-read-only 选项控制, 也可以通过 CONFIG SET 命令来开启或关闭这个模式。

只读从服务器会拒绝执行任何写命令, 所以不会出现因为操作失误而将数据不小心写入到了从服务器的情况。

主服务器只在有至少 N 个从服务器的情况下,才执行写操作

从 Redis 2.8 开始, 为了保证数据的安全性, 可以通过配置, 让主服务器只在有至少 N 个当前已连接从服务器的情况下, 才执行写命令。

从服务器以每秒一次的频率 PING 主服务器一次, 并报告复制流的处理情况。主服务器会记录各个从服务器最后一次向它发送 PING 的时间。用户可以通过配置 ` min-slaves-max-lag 指定网络延迟的最大值 , 以及执行写操作所需的至少从服务器数量 min-slaves-to-write` 。

如果至少有 min-slaves-to-write 个从服务器, 并且这些服务器的延迟值都少于 min-slaves-max-lag 秒, 那么主服务器就会执行客户端请求的写操作。

Ref

Comments