redis高可用之主从复制

redis高可用

redis主从

CAP原理

  • C:一致性
  • A:可用性
  • P:分区容忍性

分布式系统的节点往往都是分布在不同的机器上进行网络隔离开的,这意味着必然会有网络断开的风险,这个网络断开的场景的专业词汇叫网络分区。
当网络分区发生时,两个分布式节点之间无法进行通信,我们对一个节点的修改操作无法同步到另一个节点,所以数据的一致性将无法满足。除非我们牺牲可用性,也就是暂停分布式服务,在网络分区发生时不再提供修改数据的功能,直到网络状况完全恢复正常再继续对外提供服务。
CAP:当网络分区发生时,一致性和可用性两难全。

最终一致

redis的主从数据都是异步同步的,所以分布式redis不满足一致性的要求。当客户端在redis的主节点修改了数据后立即返回,即使在主从网络断开的情况下,主节点依旧可以正常对外提供修改服务,所以redis满足可用性。
redis保证最终一致性,从节点会努力追赶主节点,最终从节点的状态会和主节点的状态保持一致。如果网络断开了,主从节点的数据会出现大量不一致,但一旦网络恢复,从节点会采用多种策略努力追赶,继续尽力保持和主节点一致。

主从同步

如何进行主从同步

需要注意,主从复制的开启,完全是在从节点发起的;不需要我们在主节点做任何事情。
从节点开启主从复制,有3种方式:

  1. 配置文件

在从服务器的配置文件中加入:slaveof

  1. 启动命令

redis-server启动命令后加入 —slaveof

  1. 客户端命令

Redis服务器启动后,直接通过客户端执行命令:slaveof ,则该Redis实例成为从节点。

断开复制:
通过slaveof 命令建立主从复制关系以后,可以通过slaveof no one断开。需要注意的是,从节点断开复制后,不会删除已有的数据,只是不再接受主节点新的数据变化。
从节点执行slaveof no one后,打印日志如下所示;可以看出断开复制后,从节点又变回为主节点:
[image:78C4F792-4EEB-4F44-9DAF-96837A296B27-1128-000018F29E3D7E5F/493BD180-F46A-48EF-A661-95A2643B9EF2.png]

主节点打印日志如下:
[image:AD5036CA-AE6F-45C4-8E8D-F82BCD908CA5-1128-000018F724596E6C/C280DF44-8820-41AE-A4C9-1D4D0261101E.png]

主从同步的步骤

需要注意的是,在数据同步阶段之前,从节点是主节点的客户端,主节点不是从节点的客户端;而到了这一阶段及以后,主从节点互为客户端。原因在于:在此之前,主节点只需要响应从节点的请求即可,不需要主动发请求,而在数据同步阶段和后面的命令传播阶段,主节点需要主动向从节点发送请求(如推送缓冲区中的写命令),才能完成复制。

主从同步分为 2 个步骤:同步和命令传播

  • 同步:将从服务器的数据库状态更新成主服务器当前的数据库状态。
  • 命令传播:当主服务器数据库状态被修改后,导致主从服务器数据库状态不一致,此时需要让主从数据同步到一致的过程。

这里需要提前说明一下:在 Redis 2.8 版本之前,进行主从复制时一定会顺序执行上述两个步骤,而从 2.8 开始则可能只需要执行命令传播即可

同步阶段
  • 全量复制
  1. 从服务器向主服务器发送 sync 命令
  2. 收到 sync 命令后,主服务器执行 bgsave 命令,用来生成 rdb 文件,并在一个缓冲区中记录从现在开始执行的写命令。
  3. bgsave 执行完成后,将生成的 rdb 文件发送给从服务器,用来给从服务器更新数据
  4. 主服务器再将缓冲区记录的写命令发送给从服务器,从服务器执行完这些写命令后,此时的数据库状态便和主服务器一致了。

2A79B087-D35E-40DB-9E89-E529B1CAB535.png

全量复制是非常重型的操作:

  1. 主服务器需要执行bgsave命令来生成RDB文件,这个生成操作会耗费主服务器大量的cpu,内存和磁盘io资源。
  2. 主服务器需要将自己生成的RDB文件发送给从服务器,这个发送操作会耗费主服务器大量的网络资源(带宽和流量),并对主服务器响应命令请求的时间产生影响。
  3. 接收到RDB文件的从服务器需要清空老数据,载入主服务器发来的RDB文件,并且在载入期间从服务器会因为阻塞而没办法处理命令请求。
  • 部分复制

部分复制的实现,依赖于三个重要的概念:

  1. 主从服务器的复制偏移量
  2. 主服务器的复制积压缓冲区
  3. 服务器的运行 id(run id)
  • 复制偏移量

执行复制的主从服务器都会分别维护各自的复制偏移量:
主服务器每次向从服务器传播 n 个字节数据时,都会将自己的复制偏移量加 n。
从服务器接受主服务器传来的数据时,也会将自己的复制偏移量加 n
举个例子:
若当前主服务器的复制偏移量为 10000,此时向从服务器传播 30 个字节数据,结束后复制偏移量为 10030。
这时,从服务器还没接收这 30 个字节数据就断线了,然后重新连接上之后,该从服务器的复制偏移量依旧为 10000,说明主从数据不一致,此时会向主服务器发送 psync 命令。
那么主服务器应该对从服务器执行完整重同步还是部分重同步呢?如果执行部分重同步的话,主服务器又如何知道同步哪些数据给从服务器呢?
以下答案都和复制积压缓冲区有关

  • 复制积压缓冲区

复制积压缓冲区是由主节点维护的、固定长度的、先进先出(FIFO)队列,默认大小1MB;当主节点开始有从节点时创建,其作用是备份主节点最近发送给从节点的数据。注意,无论主节点有一个还是多个从节点,都只需要一个复制积压缓冲区。
在命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量(offset)。由于复制积压缓冲区定长且是先进先出,所以它保存的是主节点最近执行的写命令;时间较早的写命令会被挤出缓冲区
由于该缓冲区长度固定且有限,因此可以备份的写命令也有限,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。反过来说,为了提高网络中断时部分复制执行的概率,可以根据需要增大复制积压缓冲区的大小(通过配置repl-backlog-size);例如如果网络中断的平均时间是60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见,可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制。
从节点将offset发送给主节点后,主节点根据offset和缓冲区大小决定能否执行
部分复制:

* 如果offset偏移量之后的数据,仍然都在复制积压缓冲区里,则执行部分复制;
* 如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤出),则执行全量复制。
  • run id

运行 id 是在进行初次复制时,主服务器将会将自己的运行 id 发送给从服务器,让其保存起来。
当从服务器断线重连后,从服务器会将这个运行 id 发送给刚连接上的主服务器。
若当前服务器的运行 id 与之相同,说明从服务器断线前复制的服务器就是当前服务器,主服务器可以尝试执行部分同步;若不同则说明从服务器断线前复制的服务器不是当前服务器,主服务器直接执行完整重同步。

psync命令的执行过程可以参见下图(图片来源:《Redis设计与实现》):
5BDFE612-71D5-4CE0-BF6F-C3E268703B9D.png

命令传播

在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:PING和REPLCONF ACK。心跳机制对于主从复制的超时判断、数据安全等有作用

经过同步操作,此时主从的数据库状态其实已经一致了,但这种一致的状态的并不是一成不变的。
在完成同步之后,也许主服务器马上就接受到了新的写命令,执行完该命令后,主从的数据库状态又不一致。
为了再次让主从数据库状态一致,主服务器就需要向从服务器执行命令传播操作 ,即把刚才造成不一致的写命令,发送给从服务器去执行。从服务器执行完成之后,主从数据库状态就又恢复一致了

PING(主 -> 从)

每隔指定的时间,主节点会向从节点发送PING命令,这个PING命令的作用,主要是为了让从节点进行超时判断。
PING发送的频率由repl-ping-slave-period参数控制,单位是秒,默认值是10s。

REPLCONF ACK(从 -> 主)

当完成了同步之后,主从服务器就会进入命令传播阶段,此时从服务器会以每秒 1 次的频率,向主服务器发送命令:REPLCONF ACK 其中 replication_offset 是从服务器当前的复制偏移量

发送这个命令主要有三个作用:

  • 检测主从服务器的网络状态,在主节点中使用info Replication,可以看到其从节点的状态中的lag值,代表的是主节点上次收到该REPLCONF ACK命令的时间间隔,在正常情况下,该值应该是0或1
  • 检测命令丢失(若丢失,主服务器会将丢失的写命令重新发给从服务器)
  • 辅助保证从节点的数量和延迟:Redis主节点中使用min-slaves-to-write和min-slaves-max-lag参数,来保证主节点在不安全的情况下不会执行写命令;所谓不安全,是指从节点数量太少,或延迟过高。例如min-slaves-to-write和min-slaves-max-lag分别是3和10,含义是如果从节点数量小于3个,或所有从节点的延迟值都大于10s,则主节点拒绝执行写命令。而这里从节点延迟值的获取,就是通过主节点接收到REPLCONF ACK命令的时间来判断的,即前面所说的info Replication中的lag值。

参考文章

https://zhuanlan.zhihu.com/p/...
https://www.cnblogs.com/kisme...

你可能感兴趣的:(redis)