Redis集群【主从复制】

133 阅读7分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情

要想设计一个高可用的redis服务,一定要从redis的多服务节点来考虑。比如redis 的主从复制、哨兵机制、切片集群

主从复制

主从复制的实现方案就是,将一台redis服务器,同步数据到多台redis服务器上,即一主多从模式,主从之间采用多是读写分离模式。

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

所有的修改都在主服务器,然后将最新的数据同步给从服务器,这样就使得主从数据一致。

主从服务器之间的命令复制是异步的。

主服务器在收到写命令后,不会等从服务器执行完才返回结果给客户端,而是自己执行完就返回,如果从服务器还没有执行完,主从服务器之间的数据就不一致了 。而且,无法实现强一致性保证,数据不一致是难以避免的。

但是同步其实没有那么简单。

第一次同步

首先需要确认谁是主服务器,谁是从服务器。

relicaof在需要成为从服务器的服务器上执行此命令

# 服务器 B 执行这条命令
replicaof <服务器 A 的 IP 地址> <服务器 A 的 Redis 端口号>

然后b就成为了a的从服务器

然后进行第一次同步

同步的过程分为 3 个阶段:

  1. 建立连接、协商同步
  2. 主服务器同步数据给从服务器
  3. 主服务器发送新的写操作给从服务器

第一阶段:建立链接、协商同步

执行replicaof命令后,从服务器就会给主服务器发送psync命令,表示要进行数据同步。

psync命令包含两个参数: 主服务器的runID和复制进度offset

  • runID:每个redis启动时都会产生一个随机的ID来唯一标识自己,第一次同步不知道runID,所以设置为?
  • offset,表示复制的进度,第一次同步没有进度,所以标识为-1

主服务器收到psync后,返回fullsync,表示进行全量复制。并且记录runID和offset。第一阶段为了全量复制做好了准备。

第二阶段:主服务器同步数据给从服务器

主服务器会执行bgsave命令(非阻塞)来生成RDB文件,然后发送给从服务器。

从服务器收到RDB后,会清空当前数据,然后载入RDB文件。

但是,这期间的写操作命令,没有记录到RDB文件中,这时主从数据就不一致了。

为了解决这个问题,redis将主服务器在

  • 生成RDB期间
  • 发送RDB期间
  • 从服务器加载RDB期间收到的写操作命令,写入到了replication buffer缓冲区。

第三阶段:主服务器发送新的写操作给从服务器

等从服务器加载完RDB后,主服务器将replication buffer缓冲区的写命令发送给从服务器,然后执行,主从数据就一致了。

到这里,第一次同步工作结束。

命令传播

主从服务器,在第一次同步之后双发会维护一个TCP连接。

后续主服务器,可以通过这个连接继续将写操作命令传播给从服务器,然后从服务执行保证数据一致。

该TCP连接是长连接,避免连接断开频繁所带来的性能开销。

这是基于长连接的命令传播,用于保证主从服务器的数据一致性。

分摊主服务器的压力

当从服务器数量太大,全部直接与主服务器进行全量同步的话,会导致两个问题:

  • 因为是通过bgsave来生成RDB文件,所以主服务器会fork()子进程,如果服务器内存数据大,fork()会阻塞主线程
  • 传输RDB文件会占用主服务带宽,所以过多从服务器的话会影响主服务器响应

就好像刚开始的创业公司人少老板可以直接领导下属,人多了之后需要经理人。

所以redis也一样,从服务可以拥有自己的从服务器。

主服务器生成RDB和传输RDB的压力可以分摊到经理的从服务器上。

同样在从服务器的从服务器上执行

replicaof <目标服务器的IP> 6379

成为目标服务器的从服务器。

增量复制

主从之间采用长连接进行命令传播。

当网络断开后,命令无法传播,数据可能就不一致了,

客户端可能会读到旧值。

如果网络恢复后,怎噩梦继续保持主从数据一致呢?

2.8版本以前,再进行一次全量复制,开销太大。

2.8以后,将采用增量复制的方式继续同步,也就是只会传播断连期间的写操作命令。

3个步骤:

  1. 在恢复网络后,会发送psync命令给主服务器,此时的psync命令里的offset参数不是-1。
  2. 主服务器收到命令后,用continue响应命令,告诉从服务器接下来使用增量复制的方式同步
  3. 然后主服务将断连期间的写命令发送给从服务器,执行。

主服务器怎么知道要将哪些增量数据发送给从服务器呢?

  • repl_backlog_buffer 一个环形缓冲区,用于主从服务器断连后,从中找到差异的数据。
  • replication offset标识上面缓冲区的同步进度,主从都有个自的偏移量,主服务使用master_repl_offset来记录写的位置,从服务器使用slave_repl_offset记录读的位置。

repl_backog_buffer什么时候写入呢?

主服务器命令传播时,不仅会发送写命令给从服务器,还会将写命令写入到repl_backlog_buffer缓冲区里,因此缓冲区会保存最近传播到写命令。

断连重连后,从服务会通过pysnc发送自己的复制偏移量slave_repl_offset,主服务器根据master_repl_offset和slave_repl_offset之间的差距决定执行增量同步还是全量同步:

  • 如果要读取的数据还在缓冲区内,将采用增量同步
  • 不在就是全量同步

如果采用增量,会将差异数据放入replication buffer缓冲区(缓存将要传播给从服务器的命令)

repl_backlog_buffer缓冲区的默认大小时1M,并且由于是一个环形缓冲区,所以当缓冲区写满后,主服务器继续写入的话,就会覆盖之前的数据。

如果主服务器的写入速度远超从服务器的读速度,缓存区会被快速覆盖。那么重连后,就会采用全量同步损耗很大。

为了避免频繁全量同步,减少被覆盖的几率

repl_backlog_buffer应该多大?

推算

second * write_size_per_second

服务器断线后重新连接上主服务器所需的平均时间(秒)

主服务器平均每秒产生的写命令的数据量大小

为了应对突发状况,设置为上面的两倍

通过此参数 repl-backlog-size 1mb

总结

主从复制有三种模式:

全量复制、基于长连接的命令传播、增量复制。

第一次同步,采用全量复制。

然后维护一个长连接TCP,进行命令传播保证数据一致性

如果