面试宝典系列-读《深刻学习Redis(3):主从复制》概要

原文链接:深刻学习Redis(3):主从复制html

默认状况下,每台Redis服务器都是主节点;且一个主节点能够有多个从节点(或没有从节点),但一个从节点只能有一个主节点。redis

主从复制的做用主要包括:数据库

  1. 数据冗余:主从复制实现了数据的热备份,是持久化以外的一种数据冗余方式。
  2. 故障恢复:当主节点出现问题时,能够由从节点提供服务,实现快速的故障恢复;其实是一种服务的冗余。
  3. 负载均衡:在主从复制的基础上,配合读写分离,能够由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用链接主节点,读Redis数据时应用链接从节点),分担服务器负载;尤为是在写少读多的场景下,经过多个从节点分担读负载,能够大大提升Redis服务器的并发量。
  4. 高可用基石:除了上述做用之外,主从复制仍是哨兵和集群可以实施的基础,所以说主从复制是Redis高可用的基础。

主从复制的开启,彻底是在从节点发起的;不须要咱们在主节点作任何事情。服务器

主从复制过程大致能够分为3个阶段:链接创建阶段(即准备阶段)、数据同步阶段、命令传播阶段;网络

一、 链接创建阶段 

步骤1:保存主节点信息:masterhost和masterport。slaveof是异步命令,从节点完成主节点ip和port的保存后,向发送slaveof命令的客户端直接返回OK,实际的复制操做在这以后才开始进行。并发

步骤2:创建socket链接:负载均衡

从节点每秒1次调用复制定时函数replicationCron(),若是发现了有主节点能够链接,便会根据主节点的ip和port,建立socket链接。若是链接成功,则:异步

从节点:为该socket创建一个专门处理复制工做的文件事件处理器,负责后续的复制工做,如接收RDB文件、接收命令传播等。socket

主节点:接收到从节点的socket链接后(即accept以后),为该socket建立相应的客户端状态,并将从节点看作是链接到主节点的一个客户端,后面的步骤会以从节点向主节点发送命令请求的形式来进行。函数

步骤3:发送ping命令

从节点成为主节点的客户端以后,发送ping命令进行首次请求,目的是:检查socket链接是否可用,以及主节点当前是否可以处理请求。

从节点发送ping命令后,可能出现3种状况:

  • 返回pong:说明socket链接正常,且主节点当前能够处理请求,复制过程继续。
  • 超时:必定时间后从节点仍未收到主节点的回复,说明socket链接不可用,则从节点断开socket链接,并重连。
  • 返回pong之外的结果:若是主节点返回其余结果,如正在处理超时运行的脚本,说明主节点当前没法处理命令,则从节点断开socket链接,并重连。

步骤4:身份验证

若是从节点中设置了masterauth选项,则从节点须要向主节点进行身份验证;没有设置该选项,则不须要验证。从节点进行身份验证是经过向主节点发送auth命令进行的,auth命令的参数即为配置文件中的masterauth的值。

若是主节点设置密码的状态,与从节点masterauth的状态一致(一致是指都存在,且密码相同,或者都不存在),则身份验证经过,复制过程继续;若是不一致,则从节点断开socket链接,并重连。

步骤5:发送从节点端口信息

身份验证以后,从节点会向主节点发送其监听的端口号(前述例子中为6380),主节点将该信息保存到该从节点对应的客户端的slave_listening_port字段中;该端口信息除了在主节点中执行info Replication时显示之外,没有其余做用。

二、数据同步阶段

主从节点之间的链接创建之后,即可以开始进行数据同步,该阶段能够理解为从节点数据的初始化。具体执行的方式是:从节点向主节点发送psync命令(Redis2.8之前是sync命令),开始同步。

数据同步阶段是主从复制最核心的阶段,根据主从节点当前状态的不一样,能够分为全量复制和部分复制。

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

3. 命令传播阶段

数据同步阶段完成后,主从节点进入命令传播阶段;在这个阶段主节点将本身执行的写命令发送给从节点,从节点接收命令并执行,从而保证主从节点数据的一致性。

在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:PING和REPLCONF ACK。

须要注意的是,命令传播是异步的过程,即主节点发送写命令后并不会等待从节点的回复;所以实际上主从节点之间很难保持实时的一致性,延迟在所不免。

【数据同步阶段】全量复制和部分复制

  1. 全量复制:用于初次复制或其余没法进行部分复制的状况,将主节点中的全部数据都发送给从节点,是一个很是重型的操做。
  2. 部分复制:用于网络中断等状况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效。须要注意的是,若是网络中断时间过长,致使主节点没有可以完整地保存中断期间执行的写命令,则没法进行部分复制,仍使用全量复制。

全量复制的过程

  1. 从节点判断没法进行部分复制,向主节点发送全量复制的请求;或从节点发送部分复制的请求,但主节点判断没法进行全量复制;具体判断过程须要在讲述了部分复制原理后再介绍。
  2. 主节点收到全量复制的命令后,执行bgsave,在后台生成RDB文件,并使用一个缓冲区(称为复制缓冲区)记录从如今开始执行的全部写命令
  3. 主节点的bgsave执行完成后,将RDB文件发送给从节点;从节点首先清除本身的旧数据,而后载入接收的RDB文件,将数据库状态更新至主节点执行bgsave时的数据库状态
  4. 主节点将前述复制缓冲区中的全部写命令发送给从节点,从节点执行这些写命令,将数据库状态更新至主节点的最新状态
  5. 若是从节点开启了AOF,则会触发bgrewriteaof的执行,从而保证AOF文件更新至主节点的最新状态

经过全量复制的过程能够看出,全量复制是很是重型的操做:

  • 主节点经过bgsave命令fork子进程进行RDB持久化,该过程是很是消耗CPU、内存(页表复制)、硬盘IO的;
  • 主节点经过网络将RDB文件发送给从节点,对主从节点的带宽都会带来很大的消耗
  • 从节点清空老数据、载入新RDB文件的过程是阻塞的,没法响应客户端的命令;若是从节点执行bgrewriteaof,也会带来额外的消耗

部分复制,三个概念:

(1)复制偏移量

主节点和从节点分别维护一个复制偏移量(offset),表明的是主节点向从节点传递的字节数;主节点每次向从节点传播N个字节数据时,主节点的offset增长N;从节点每次收到主节点传来的N个字节数据时,从节点的offset增长N。

offset用于判断主从节点的数据库状态是否一致:若是两者offset相同,则一致;若是offset不一样,则不一致,此时能够根据两个offset找出从节点缺乏的那部分数据。例如,若是主节点的offset是1000,而从节点的offset是500,那么部分复制就须要将offset为501-1000的数据传递给从节点。而offset为501-1000的数据存储的位置,就是下面要介绍的复制积压缓冲区。

(2)复制积压缓冲区

复制积压缓冲区是由主节点维护的、固定长度的、先进先出(FIFO)队列,默认大小1MB;当主节点开始有从节点时建立,其做用是备份主节点最近发送给从节点的数据。注意,不管主节点有一个仍是多个从节点,都只须要一个复制积压缓冲区。

在命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,做为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中的每一个字节对应的复制偏移量(offset)。因为复制积压缓冲区定长且是先进先出,因此它保存的是主节点最近执行的写命令;时间较早的写命令会被挤出缓冲区。

因为该缓冲区长度固定且有限,所以能够备份的写命令也有限,当主从节点offset的差距过大超过缓冲区长度时,将没法执行部分复制,只能执行全量复制。反过来讲,为了提升网络中断时部分复制执行的几率,能够根据须要增大复制积压缓冲区的大小(经过配置repl-backlog-size);例如若是网络中断的平均时间是60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见,能够设置为12MB,来保证绝大多数断线状况均可以使用部分复制。

从节点将offset发送给主节点后,主节点根据offset和缓冲区大小决定可否执行部分复制:

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

(3)服务器运行ID(runid)

每一个Redis节点(不管主从),在启动时都会自动生成一个随机ID(每次启动都不同),由40个随机的十六进制字符组成;runid用来惟一识别一个Redis节点。经过info Server命令,能够查看节点的runid:

主从节点初次复制时,主节点将本身的runid发送给从节点,从节点将这个runid保存起来;当断线重连时,从节点会将这个runid发送给主节点;主节点根据runid判断可否进行部分复制:

  • 若是从节点保存的runid与主节点如今的runid相同,说明主从节点以前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的状况);
  • 若是从节点保存的runid与主节点如今的runid不一样,说明从节点在断线前同步的Redis节点并非当前的主节点,只能进行全量复制。

redis读写分离,能够实现redis的读负载均衡。

主从复制延迟与不一致问题的优化措施:优化主从节点之间的网络环境(如在同机房部署);监控主从节点延迟(经过offset)判断,若是从节点延迟过大,通知应用再也不经过该从节点读取数据;使用集群同时扩展写负载和读负载等。

复制中断问题

主从节点超时是复制中断的缘由之一,除此以外,还有其余状况可能致使复制中断,其中最主要的是复制缓冲区溢出问题。

复制缓冲区溢出

前面曾提到过,在全量复制阶段,主节点会将执行的写命令放到复制缓冲区中,该缓冲区存放的数据包括了如下几个时间段内主节点执行的写命令:bgsave生成RDB文件、RDB文件由主节点发往从节点、从节点清空老数据并载入RDB文件中的数据。当主节点数据量较大,或者主从节点之间网络延迟较大时,可能致使该缓冲区的大小超过了限制,此时主节点会断开与从节点之间的链接;这种状况可能引发全量复制->复制缓冲区溢出致使链接中断->重连->全量复制->复制缓冲区溢出致使链接中断……的循环。解决办法:增大复制缓冲区

相关文章
相关标签/搜索