Redis主从复制(Replication)

Redis主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master)以写为主,后者称为从节点(slave)以读为主,当master数据变化的时候,自动将新的数据异步同步到其他slave库。数据的复制是单向的,只能由主节点到从节点。

主从库之间采用的是读写分离的方式:

  • 读操作:主库、从库都可以接收;
  • 写操作:首先到主库执行,然后,主库将写操作同步给从库。
Redis集群slaves写操作同步读操作写操作读操作读操作masterslaveAslaveBClients客户端客户端

主从复制的作用主要包括:

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

配置主从复制

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

如果master配置了requirepass,slave需要再配置文件中加上matser的密码使用msaterauth配置

masterauth <master-password>

开启主从复制,有3种方式:

  1. 配置文件:

    在从服务器的配置文件中加入:slaveof <masterip> <masterport>,Redis5.0之后使用replicaof <masterip> <masterport>

  2. 启动命令:

    redis-server启动命令后加入--slaveof <masterip> <masterport>

  3. 客户端命令:

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

主从复制原理

Redis在2.8版本之前只有全量复制,而2.8版本后有全量和增量复制。

全量复制

当启动多个Redis实例的时候,它们相互之间就可以通过replicaofRedis5.0之前使用slaveof)命令形成主库和从库的关系,之后会按照三个阶段完成数据的第一次同步。

MasterSlave第一阶段:建立连接,协商同步执行replicaof ip portpsync ? -1FULLRESYNC {runID} {offset}保存主节点信息第二阶段:主库同步数据给从库执行bgsave 生成RDB发送RDB文件清空现有数据,加载RDB第三阶段:主库发送新写命令给从库replication buffer发送replication buffer加载replication bufferMasterSlave

第一阶段:建立连接,协商同步

主要是为全量复制做准备。在这一步,从库和主库建立起连接,并告诉主库即将进行同步,主库确认回复后,主从库间就可以开始同步了。

从库给主库发送psync命令,表示要进行数据同步,主库根据这个命令的参数来启动复制psync命令包含了主库的runID和复制进度offset两个参数。

  1. runID是每个Redis实例启动时都会自动生成的一个随机ID。当从库和主库第一次复制时,因为不知道主库的runID,所以将runID设为“?”。
  2. offset,此时设为-1,表示第一次复制。
  3. 主库收到psync命令后,会用FULLRESYNC响应命令带上两个参数:主库runID和主库目前的复制进度offset,返回给从库。
  4. 从库收到响应后,会记录下这两个参数。

FULLRESYNC响应表示第一次复制采用的全量复制,也就是说,主库会把当前所有的数据都复制给从库。

第二阶段:主库同步数据给从库

主库将所有数据同步给从库。从库收到数据后,在本地完成数据加载。这个过程依赖于内存快照生成的RDB文件。

  1. 主库执行bgsave命令,生成RDB文件,接着将文件发给从库。

  2. 从库接收到RDB文件后,会先清空当前数据库,然后加载RDB文件。

    从库可能在通过replicaof命令开始和主库同步之前保存了其他数据。从库需要先把当前数据库清空,避免之前数据的影响。

  3. 在主库将数据同步给从库的过程中,主库不会被阻塞,仍然可以正常接收请求。否则,Redis的服务就被中断了。但是,这些请求中的写操作并没有记录到刚刚生成的RDB文件中。

    为了保证主从库的数据一致性,主库会在内存中用专门的replication buffer,记录RDB文件生成后收到的所有写操作。

第三阶段:主库发送新写命令给从库

主库会把第二阶段执行过程中新收到的写命令,再发送给从库。具体的操作是,

  1. 当主库完成RDB文件发送后,就会把此时replication buffer中的修改操作发给从库。
  2. 从库再重新执行这些操作。这样一来,主从库就实现同步了。

增量复制

如果在主从库同步过程中出现网络波动,从库就会和主库重新进行一次全量复制,开销非常大。从Redis2.8开始,网络断了之后,主从库会采用增量复制的方式继续同步。

MasterSlave网络断开repl_backlog_buffer网络恢复连接psync {runID} {offset}replication buffer发送offet之后的replication buffer执行网络断开后增加的replication bufferMasterSlave
  • repl_backlog_buffer:是为了从库断开之后,能找到主从差异数据而设计的环形缓冲区,从而避免全量复制带来的性能开销。

    如果从库断开时间太久,repl_backlog_buffer环形缓冲区被主库的写命令覆盖了,那么从库连上主库后只能乖乖地进行一次全量复制,所以repl_backlog_buffer配置尽量大一些,可以降低主从断开后全量复制的概率。而在repl_backlog_buffer中找主从差异的数据后,就用replication buffer发送给从库。

  • replication buffer:Redis和客户端,从库通信都需要分配一个内存buffer进行数据交互。从库和客户端都是一个“Client”,每个“Client”连接,Redis都会分配一个client buffer,所有数据交互都是通过这个buffer进行的。

    1. Redis先把数据写到这个buffer中。
    2. 再把buffer中的数据发到client socket中发送出去。

    所以在增量同步时,从库作为一个client,也会分配一个buffer,只不过这个buffer专门用来传播用户的写命令到从库,保证主从数据一致,通常把它叫做replication buffer

如果在网络断开期间,repl_backlog_buffer环形缓冲区写满之后:

  1. 一个从库如果和主库断连时间过长,造成它在主库repl_backlog_bufferslave_repl_offset位置上的数据已经被覆盖掉了,此时从库和主库间将进行全量复制。
  2. 每个从库会记录自己的slave_repl_offset,每个从库的复制进度也不一定相同。在和主库重连进行恢复时,从库会通过psync命令把自己记录的slave_repl_offset发给主库,主库会根据从库各自的复制进度,来决定这个从库可以进行增量复制,还是全量复制。

主从复制的缺点

  1. 由于所有写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave上有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重。Slave服务数量的增加也会使这个问题更加严重。

    ClustersRedis-MasterRedis-SlaveRedis-SlaveRedis-SlaveRedis-Slave
  2. 如果Master宕机了,默认情况下不会在Slave节点中自动重新选一个Master

参考文章