Redis主从复制

定义

单个Redis数据库很容易出现单点故障和容量瓶颈问题,Redis支持将主数据库(Master)上的数据复制多份到在多个不同的从数据库(Slave)上,数据流向是单向的 - Master -> Slave。通常主数据进行数据写入操作,从数据库进行读操作,实现数据的读写分离。

实现方式

  1. 命令方式

    • slaveof slavehost # 将 slavehost 将复制为当前Redis数据库的从数据库,异步操作,会将数据库的数据进行清除。
      e.g., 127.0.0.1: 6380 > slaveof 127.0.0.1: 6379 # 127.0.0.1: 6379将会成为127.0.0.1: 6380的主数据库
    • slaveof no one # 取消当前命令执行数据库的复制,以前主数据库已经同步的数据不会清除。
    • info replication # 当前数据库是主还是从数据库的相关信息
  2. 配置 - redis.conf,配置之后需重启

    • slaveof masterip masterport
    • slave-read-only yes

命令传播

执行完全量复制之后,主从数据库之间数据库状态已经相同了。但这个状态并非一成不变,如果主数据库执行了写操作,那么主数据库的数据库状态就会修改,并导致主从数据库状态不再一致。所以为了让主从数据库再次回到一致状态,主数据库需要对从数据库执行命令传播操作:主数据库会将自己执行的写命令,也即是造成主从数据库不一致的那条写命令,发送给从数据库执行,当从数据库执行了相同的写命令之后,主从数据库将再次回到一致状态。

全量复制

  1. 复制过程
    • 从数据库向主数据库发送PSYNC命令 - 从2.8开始,SYNC替换成PSYNC,提供了完整重同步和部分重同步
    • 收到PSYNC命令后,主数据库将自身的runId和offset传给从数据库,之后主数据库执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始执行的所有写命令
    • 当主数据库的BGSAVE命令执行完毕时,主数据库会将BGSAVE命令生成的RDB文件发送给从数据库,从数据库接收并载入这个RDB文件,将自己的数据库状态更新至主数据库执行BGSAVE命令时的数据库状态
    • 主数据库将记录在缓冲区里面的所有写命令发送给从数据库,从数据库执行这些写命令,将自己的数据库状态更新至主数据库数据库当前所处的状态
  2. 开销
    • bgsave时间
    • RDB文件网络传输时间
    • 清空从数据库花费时间
    • 从数据库加载RBD文件花费的时间,并且载入期间,从数据库可能因为阻塞而无法处理客户端请求

部分复制

  1. 解决问题:处理从数据库断线后的重连问题。当从数据库在断线后重新连接主数据库时,如果条件允许,主数据库可以将主从数据库连接断开期间执行的写命令发送给从数据库,从数据库只要接收并执行这些写命令,就可以将数据库更新至主数据库当前所处的状态。而2.8以前,断连之后还要进行一次SYNC操作。
  2. 复制过程
    • 当从数据库断线后,主数据库会继续把写命令放入到复制积压缓冲区(replication backlog)中
    • 当从数据库重新连上主数据库时,从数据库会通过PSYNC命令将自己的复制偏移量(replication offset)和主数据库的运行ID(run id)发送给主数据库,主数据库会根据这个复制偏移量和运行ID来决定对从数据库执行何种同步操作
    • 如果从数据库发送的运行ID和当前连接的主数据库的运行ID相同,那么说明从数据库断线之前复制的就是当前连接的这个主数据库,主数据库可以继续尝试执行部分复制操作
    • 相反,如果从数据库发送的运行ID和当前连接的主数据库的运行ID并不相同,主数据库将对从数据库执行全量复制操作
    • 如果从数据库的复制偏移量之后的数据(也即是offset+1开始的数据)仍然存在于复制积压缓冲区里面,那么主数据库将对从数据库执行部分复制操作
    • 相反,如果offset之后的数据已经不存在于复制积压缓冲区,那么主数据库将对从数据库执行全量复制操作

扩展

  1. 复制偏移量(replication offset):主数据库和从数据库会分别维护一个复制偏移量:
    • 主数据库每次向从数据库传播N个字节的数据时,就将自己的复制偏移量的值加上N
    • 从数据库每次收到主数据库传播来的N个字节的数据时,就将自己的复制偏移量的值加上N
      对比主从数据库的复制偏移量,可知道主从数据库是否处于一致状态:
    • 如果主从数据库处于一致状态,那么主从数据库两者的偏移量总是相同的
    • 相反,如果主从数据库两者的偏移量并不相同,那么说明主从数据库并未处于一致状态
  1. 复制积压缓冲区(replication backlog):复制积压缓冲区是由主数据库维护的一个固定长度(fixed-size)先进先出(FIFO)队列。当主数据库进行命令传播时,它不仅会将写命令发送给所有从数据库,还会将写命令入队到复制积压缓冲区里面。因此,主数据库的复制积压缓冲区里面会保存着一部分最近传播的写命令,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量
    复制积压缓冲区默认大小为1MB,如果主数据库需要执行大量写命令,又或者主从数据库断线后重连接所需的时间比较长,那么Redis的部分复制功能可能会达不到想要的效果。复制积压缓冲区的最小大小可以根据公式 second*write_size_per_second 来估算:

    • second为从数据库断线后重新连接上主数据库所需的平均时间(以秒计算)
    • write_size_per_second是主数据库平均每秒产生的写命令数据量。
    • 例如,如果主数据库平均每秒产生1 MB的写数据,而从数据库断线之后平均要5秒才能重新连接上主数据库,那么复制积压缓冲区的大小就不能低于5MB。为了安全起见,可以将复制积压缓冲区的大小设为2secondwrite_size_per_second,这样可以保证绝大部分断线情况都能用部分重同步来处理,可以通过修改repl-backlog-size配置进行设置。
  2. 数据库运行ID
    每个Redis数据库,不论主数据库还是从服务,都会有自己的运行ID。运行ID在数据库启动时自动生成,由40个随机的十六进制字符组成,例53b9b28df8042fdc9ab5e3fcbbbabff1d5dce2b3。
    当从数据库对主数据库进行初次复制时,主数据库会将自己的运行ID传送给从数据库,而从数据库则会将这个运行ID保存起来。当从数据库断线并重新连上一个主数据库时,从数据库将向当前连接的主数据库发送之前保存的运行ID。

问题

  1. 读写分离
    • 数据复制延迟
    • 读到过期数据
    • 从数据库故障
  2. 主从数据库配置不一致
    • maxmemory不一致:丢失数据
    • 数据结构优化参数(例如hash-max-ziplist-entries):内存不一致
  3. 规避全量复制
    • 第一次全量复制不可避免 - 尽量低峰(夜间)进行
    • 避免主数据库runId不匹配导致的全量复制
    • 避免复制积压缓冲区不足导致的全量复制 - 修改rel_backlog_size配置
  4. 规避复制风暴
    • 单主数据库复制风暴 - 主数据库重启,多从数据库复制
    • 单机器复制风暴 - 机器有多个主数据库,机器宕机,大量全量复制。避免多个主数据库部署在一个机器上

参考资料

[1] https://coding.imooc.com/class/151.html

Author: HB
Link: http://www.huangbin.fun/Redis主从复制.html
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.