redis主从复制

单点问题

首先,我们都知道redis非常的快,并且可用性也很高,但是的话,当个redis服务器能够抗住的并发始终是有限的,因此,为了保证redis的高可用,我们通常都会采用分布式的系统来部署redis服务器,每个物理服务器上部署一个redis服务器,并且在众多redis服务器中选出一个主服务器出来(主节点),其它redis服务器都是从服务器(从节点),当“写”请求来了过后,写请求将被分配到主节点上去,而对于读请求,将被负载均衡层分配到众多从节点中的一个从节点机器上去,这样的话就减少了主节点的并发压力,同时也提高了对于客户端命令的响应时间,同时主节点会定期将自己内部的数据同步到从节点中去,从而保证主节点和从节点的数据一致。

在这里插入图片描述
更准确的说,主从模式主要是针对“读”操作进行并发量和可用性的提高,对于写操作,无论是可用性还是并发性都非常的依赖主节点,幸运的是,在实际业务业务场景中读操作往往比写操作更加频繁;

对于这样的分布式系统来说,就算挂掉某个从节点也没有影响,此时继续从其它从节点或主节点读取数据也能达到一样的效果;可要是主节点挂了,那么写操作就无法得到响应,可能会造成数据库内存在过期数据,因此对于主从结构的分布式redis服务来说,可用性是提高了,但是还没有达到非常理想的地步!
那么为什么不搞多个主节点呢?
假设搞了多个主节点,那么这几个主节点之间又如何进行通信呢?这又是个麻烦事。

配置

从实际上来说,为了达到上述这种主从模式的分布式系统,我们通常都是在一个物理机器上部署一个redis服务器,但是由于我们大多数读者手里只有一个云服务器,因此,我们可以在一个云服务器上部署多个redis服务器,但是要注意端口冲突,这里我们以6379端口的redis服务器为主节点,6380、6381端口的redis服务器为从节点,同时这3个redis服务器的配置文件也应该各自一份、数据持久化路径也应该给自隔离开;
在这里插入图片描述

建立复制

参与复制的redis实例划分为主节点和从节点。默认情况下,redis都是主节点。每个从节点只能有一个主节点,每个主节点可以有多个从节点。复制数据的流向是单向的,只能从主节点复制到从节点。

配置复制的方式有3种:

  1. 在配置文件种加入 slaveof {mastreIP} {masterPort},然后重启redis服务器生效;
  2. 在redis服务器启动过后,通过命令行输入:slaveof {masterIP} {masterPort}命令生效(这个生效只是暂时的,redis服务器重启过后会重置);
  3. 在redis-server启动的时候加入命令行参数:–slaveof {masterIP} {masterPort}(这个生效也只是暂时的,redis服务器重启过后会重置);

这里我们采用配置文件的方式来重启两个从节点:
在这里插入图片描述在这里插入图片描述
重启从节点服务器;
然后使用info replication 命令来查看一下复制情况:
在这里插入图片描述

接下来,我们来具体讲讲:info replication命令查出来的部分选项信息:

  1. role: 每个redis服务器扮演的角色,主节点自然是master、从节点就是slave
  2. connected_slaves: 主节点后面挂着的从节点的数量
  3. slave0: 从节点的部分信息;
  1. state: 从节点的状态;
  2. offset: 从节点的复制进度,相当于从节点从主节点这里复制数据的进度。
  1. master_replid: 从节点的master_replid应该和主节点的一样;对于从节点来说master_replid就是数据的来源,对于主节点来说就是一个标识;
  2. master_replid2: 如果出现了网络抖动,导致从节点和主节点断开了连接,而这个从节点自己又成为了主节点的话,那么这个master_replid2字段就会将原来的master节点的master_replid记录在master_replid2字段,如果有朝一日,这个从节点,从新与主节点建立了连接,那么这个从节点的master_replid字段会恢复成这个主节点的master_replid,这个数据就从从节点的master_replid2字段来;
  3. master_repl_offset: 是复制流中的一个偏移量,master处理完写入命令后,会把命令的字节长度做累加记录,统计在该字段。该字段也是实现部分复制的关键字段。

接下来,我们来做一些实验看看,在主节点种插入一些数据:
在这里插入图片描述
我们发现,我们在主节点插入的数据,在从节点种也能够读取到,但是对于从节点来说,从节点理论上是真能读取数据,不能插入数据:
在这里插入图片描述
但是实际上,我们也可以要通过配置选项:slave-read-only=yes将从节点修改为可读可写,但是一般在生产环境中不会这么干;

断开复制

对于从节点来说,如果它想断开与主节点的复制连接的话,那么我们可以输入命令:slaveof no one
这个命令主要干两件事:

  1. 断开与主节点的复制关系;
  2. 从从节点升为主节点;
    从节点断开连接过后,不会讲已经复制的数据丢失,仍会保存,只是后续对于主节点的新增数据,就无法感应和复制了.

同理,对于从节点来说,如果我们想要切换连接的主节点的话,那么我们也可以使用命令:
slaveof {masterIp} {masterPort} 命令来完成;
这个命令主要完成四件事:

  1. 断开与主节点的复制关系;
  2. 与新的主节点建立复制关系;
  3. 删除从节点当前的所有数据;
  4. 对新节点进行复制操作;

切换主节点过后,从节点原来的数据就会被清空,在实际生产环境中,我们要尤为注意!!!

传输延迟:
主节点和从节点一般部署在不同的主机上,复制时的网络延迟就成为了我们需要考虑的问题,redis为我们提供了repl-disable-tcp-nodelay参数用于控制是否关闭TCP_NODELAY,默认是关闭的:

  1. 当关闭时,延迟会减小,但是带宽会增加。适合主从之间网络状态环境良好,例如:同机房部署;
  2. 当开启时,延迟会增加,但是带宽会降低。适合主从节点部署在不同机房,例如:跨机房部署;

拓扑

  1. 一主一从结构
    在这里插入图片描述
    这是最简单的复制拓扑结构,用于主节点出现宕机时从节点提供故障转移支持。如果出现写并发量比较高的同时,我们可以关闭主节点的持久化机制,只在从节点上开启AOF持久化机制,这样既保证了数据的完整性,同时也避免了持久化对于主节点性能的影响。但是要注意一点就是如果主节点进行了重启的话,从节点一定要先断开与主节点的连接,避免主节点重启过后将空数据同步给从节点,从而导致从节点中的数据也丢失。
  1. 一主多从结构
    在这里插入图片描述
    该结构相比于一主一从结构可用性更高,假设一主一从结构中从节点挂了,那么读写并发的压力都堆积到主节点上了,这对主节点的挑战很大,而对于一主多从则不存在这种情况,即使挂了一个从节点,对于整个redis服务的性能并没有多大影响;但是一主多从结构也有一个缺点就是,如果主节点后面挂载的从节点太多了的话,那么主节点每次都需要将同一条命令重复同步多次,非常消耗带宽。
  1. 树状主从结构
    在这里插入图片描述
    对于从节点来说,从节点也可以扮演master的角色,即从节点后面也可以挂载从节点;
    该结构的话可以减少,主节点挂载过后从节点,导致带宽消耗较大的问题,但是如果这颗树高度太高的话,那么数据同步的延迟性会比较高,可能会导致底层节点的数据和高层节点的数据差异较大。

复制原理

接下来,我们来详细介绍一下复制的大致过程:
在这里插入图片描述

  1. 保存主节点信息

当从节点接收到slaveof {masterIP} {masterPort}命令过后,从节点会保存要连接的主节点的ip地址和端口号等信息,此时还并未与主节点建立起真正的连接关系,因此此时的连接状态是:down;

  1. 主从建立socket连接

从节点内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接,从节点会建立一个socket套接字,专门用于接收主节点发送的复制命令;如果从节点无法建立连接,定时任务会无限重试直到连接成功或执行slaveof no one取消复制;

  1. 发送ping命令

连接建立完毕过后,从节点发送ping命令测试首次通信,其目的是:检测主从之间网络套接字是否可用;检测主节点当前是否可以接收处理命令;

  1. 权限验证

如果主节点设置了requirepass参数,则需要密码进行验证,从节点必须配置masterauth参数保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。

  1. 数据同步

5.1 全量同步

主从复制连接正常通信过后,对于首次与主节点建立连接的从节点来说,主节点会将自己内存中的所有数据全量同步给从节点,这步操作是非常耗时的。在redis2.8版本以前,采用sync命令来进行数据同步,2.8以后则采用psync命令来进行数据同步。

5.2 实时同步

当用户成功完成与主节点的第一次全量复制过后,这一瞬间,主节点和从节点的数据是一致的,但后续主节点仍然会源源不断的接收到新的“写命令”,那么对于此时的主节点来说,他就只需要将此时接收到的数据通过之前建立的TCP长连接及时同步给从节点即可,这样就能够及时的保证主节点和从节点之间的数据一致性了;

psync命令:
psync replicationID offset

  1. 当从节点完成了与主节点的复制通信连接建立过户,从节点会向主节点发送psync命令来拉取主节点上的数据,以完成数据同步;其中对于psync的参数中,replicationID是表示从节点要拉区的主节点的replicationID是多少,用来表示数据来源;offset表示从节点当前的同步进度;
    对于首次进行数据复制的从节点来说,他是发送的psync命令的replicationID为“?”,offset为-1;
    如果是不是第一次从主节点拉取数据,即已经从主节点拉取过数据了,那么replicationID就是主节点的replicationID,offset就是从节点已经同步的进度,主节点接收到这个命令过后会对其进行验证,验证通过后,会从从节点提供的offset位置开始继续同步数据给从节点;
  2. 主节点在接收到来自客户端的psync命令过后,会立马对其进行解析,然后进行验证,并立马给出响应;
    如果主节点回复+FULLRESYNC,那么就是告诉从节点接下来要对你进行全量复制;
    如果主节点回复+CONTINUE,那么就是告诉从节点接下来要对你进行部分复制;
    如果回复+ERR,那么说明主节点版本低于2.8,不能识别psync命令,从节点应该用sync命令对主节点进行数据拉取;

全量复制

全量复制是redis最早支持的复制方式,也是主从第一次建立复制时,必须经历的阶段。
大致流程如下:

在这里插入图片描述

  1. 从节点发送 psync 命令给主节点进⾏数据同步,由于是第⼀次进⾏复制,从节点没有主节点的运
    ⾏ ID 和复制偏移量,所以发送 psync ? -1
  2. 主节点根据命令,解析出要进⾏全量复制,回复 +FULLRESYNC 响应,在这个响应中会携带上主节点的replicationID和复制偏移量。
  3. 从节点接收主节点的运⾏信息进⾏保存。
  4. 主节点执⾏ bgsave 进⾏ RDB ⽂件的持久化。
  5. 从节点发送 RDB ⽂件给从节点,从节点保存 RDB 数据到本地硬盘。
  6. 主节点将从⽣成 RDB 到接收完成期间执⾏的写命令,写⼊复制客户端缓冲区中,等从节点保存完 RDB ⽂件后,主节点再将缓冲区内的数据补发给从节点,补发的数据仍然按照 rdb 的⼆进制格式追加写⼊到收到的 rdb ⽂件中. 保持主从⼀致性。
  7. 从节点清空⾃⾝原有旧数据。
  8. 从节点加载 RDB ⽂件得到与主节点⼀致的数据。
  9. 如果从节点加载 RDB 完成之后,并且开启了 AOF 持久化功能,它会进⾏ bgrewrite 操作,得到最近的 AOF ⽂件。

通过分析全量复制的所有流程,我们会发现全量复制是⼀件⾼成本的操作:主节点 bgsave 的时间,
RDB 在⽹络传输的时间,从节点清空旧数据的时间,从节点加载 RDB 的时间等。所以⼀般应该尽可能避免对已经有⼤量数据集的 Redis 进⾏全量复制。

有磁盘复制 vs ⽆磁盘复制(diskless)
默认情况下, 进⾏全量复制需要主节点⽣成 RDB ⽂件到主节点的磁盘中, 再把磁盘上的 RDB
⽂件通过发送给从节点.
Redis 从 2.8.18 版本开始⽀持⽆磁盘复制. 主节点在执⾏ RDB ⽣成流程时, 不会⽣成 RDB ⽂
件到磁盘中了, ⽽是直接把⽣成的 RDB 数据通过⽹络发送给从节点. 这样就节省了⼀系列的写
硬盘和读硬盘的操作开销.

部分复制

部分复制主要用于从节点在从主节点读取数据期间,突然遭遇了网络波动,导致从节点与主节点断开了连接,然后经过调整过后,从节点又恢复与主节点的连接过程。在重新建立连接过后,从节点没必要再重新进行全量同步了,由于从节点已经从主节点同步了一部分数据,因此从节点在向主节点发送pysnc同步命令时,offset参数会被设置为从节点上次次同步的位置,主节点在接收到这个命令过后会对其进行验证,验证通过,则继续从上次同步的位置,继续同步给从节点;验证失败则进行全量同步;

这个验证规则大概如下:

  1. 提取psync命令中的replicationID是否与当前主节点的replicationID是否一致,如果不一致,那么说明主节点遭遇过重启,从节点需要重新进行全量复制;(主节点每次重启的replicationID都是不一样的)
  2. 如果psync命令中的replicationID与当前主节点的replicationID一致.那么继续提取psync命令中的offset参数,如果offset偏移量在复制积压缓冲区中,那么则进行部分复制;如果不在则进行全量复制;

复制积压缓冲区:
复制积压缓冲区是保存在主节点上个的一个固定队列,默认大小为1MB,当主节点有链接的从节点是被创建,所有从节点共享这一个复制积压缓冲区;这时候主节点响应写命令时,不但会把命令发送给从节点,还会写入复制积压缓冲区;

在从节点与主节点断开连接到恢复连接这段时间内,主节点也会陆续的接收到来自客户端的“写”命令,但是由于从节点断开了连接,主节点无法将收到的命令同步给从节点,因此主机点会将收到的写命令写入到复制积压缓冲区中去,当从节点恢复连接过后,如果偏移量在复制积压缓冲区中,则从积压缓冲区中读取数据进行部分复制,如果不在,那么说明从节点断开连接的时间太久了,导致积压缓冲区中的数据被覆盖了(因为复制积压缓冲区是个有固定大小的环形队列,当环形队列被装满的时候,那么就会让新的数据覆盖旧数据,因此挤压缓冲区只能暂存短时间内接收到的命令,时间久了就会造成旧数据被覆盖),因此为例保证主从数据一致性,就需要对从节点进行全量复制了;
这也就是为什么主节点在响应命令的时候会将写命令写入到积压缓冲区中去了,为的就是当从节点断开连接又恢复连接过后,能够及时的从积压缓冲区中补救数据,避免造成全量复制。

实时复制

主从节点在建⽴复制连接后,主节点会把⾃⼰收到的 修改操作 , 通过 tcp ⻓连接的⽅式, 源源不断的传输给从节点. 从节点就会根据这些请求来同时修改⾃⾝的数据. 从⽽保持和主节点数据的⼀致性.

另外, 这样的⻓连接, 需要通过⼼跳包的⽅式来维护连接状态. (这⾥的⼼跳是指应⽤层⾃⼰实现的⼼跳,⽽不是 TCP ⾃带的⼼跳)

  1. 主从节点彼此都有⼼跳检测机制,各⾃模拟成对⽅的客⼾端进⾏通信.
  2. 主节点默认每隔 10 秒对从节点发送 ping 命令,判断从节点的存活性和连接状态。
  3. 从节点默认每隔 1 秒向主节点发送 replconf ack {offset} 命令,给主节点上报⾃⾝当前的复制偏移量。
  4. 如果主节点发现从节点通信延迟超过 repl-timeout 配置的值(默认 60 秒),则判定从节点下线,断开复制客⼾端连接。从节点恢复连接后,⼼跳机制继续进⾏。

主从复制总结

  1. 主从复制结构,相比于单点结构来说提高了服务的可用性和可靠性,同时也调高了单点结构的服务上限;
  2. redis通过主从复制结构能够实现主节点的多个副本,及时从节点挂了一个也不影响整体的可靠性和数据丢失问题;
  3. 主节点专门进行写,从节点专门进行读,可以减轻主节点的访问压力,提高整个系统的可用性;
  4. 复制⽀持多种拓扑结构,可以在适当的场景选择合适的拓扑结构。
  5. 复制分为全量复制, 部分复制和实时复制。
  6. 主从节点之间通过⼼跳机制保证主从节点通信正常和数据⼀致性。
  7. 当然,主从复制也有一些缺点就是:如果主节点后面挂了太多从节点的话,那么对于主节点来说,它每次都需要将相同的数据,进行多次传输,带宽消耗较大;而如果整个系统采用树形结构的话,树的高度太高了,又会造成数据延迟的问题;
  8. 主从复制最致命的一点就是,主节点挂了怎么办?在主从结构中主节点挂了,从节点是不会晋升为主节点的,只有我们手工干预,重新启动主节点。这无疑是个麻烦事,因为我们也无法预测主节点什么时候会挂?并且就算知道了,手工恢复也需要一定的时间。在这瞬息秒变的时代,服务器长时间起不来或频繁挂机可是个严重危机,为了解决主节点挂了的问题,redis官方提出了哨兵的机制解决这一问题,关于哨兵,我们将后续进行讲解.
  • 12
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南猿北者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值