Redis复制

什么是redis的复制,为什么要有复制功能,全量复制有什么问题,部分复制又解决了哪些问题。

复制就是将redis节点中的数据复制完同步到其他节点中,在redis中参与复制一般有主节点(master)以及从节点(slave),将数据从主节点复制到从节点,并且复制的方式是单向的,不能逆向复制,在建立复制关系之后主节点的数据会同步到从节点直到解除主从关系。不论是哨兵模式还是redis集群都是在复制的基础上完成的。

记忆点

  1. 使用方式:建立、断开复制、安全性、只读性
  2. 适用场景:哨兵、集群
  3. 复制原理:建立复制、全量复制、部分复制、心跳
  4. 常见问题:读写分离、数据不一致、避免全量复制

使用方式

  1. 建立复制
    将具有复制关系的两个redis的节点一个称为master节点,另一个称为salve节点。主节点可以拥有多个从节点,从节点主能拥有一个主节点。
    建立连接方式:
    • 配置文件:在redis从节点的配置文件中添加saveof ${masterHost} ${masterPort}配置
    • 启动命令:在redis启动命令中加入 --saveof ${masterHost} ${masterPort}命令
    • 运行时命令:在redis运行时直接使用 salveof ${masterHost} ${masterPort}命令
  2. 断开复制
    在从节点中执行salve no one,断开复制不会清除之前同步成功的数据。
  3. 切换主节点
    在一个已经有主节点的从节点中执行slaveof ${newHost} ${newPort}就会切换到新的节点,但是之前同步的数据会被清空掉,这个和断开复制有点区别。
  4. 安全性
    在建立主从复制关系的时候,可以考虑数据的重要性,决定是否需要对主节点添加requirepass配置,这样在从节点建立复制关系的时候就会需要输入一个密码masterauth,这样的话可以提高数据的安全性。
  5. 只读性
    在默认的情况下从节点的数据是只读的,是不允许修改数据的,可以通过修改salve-read-only配置修改从节点的读写模式,但是这样会导致从节点和主节点数据不一致,不建议试用。

复制拓扑结构

  • 一主一从:可以用来解决单点故障问题,并且当写命令并发量大且开启AOF持久化时,可以将AOF持久化的放在从节点上。但是由于主节点没有开启AOF,所以在重启后数据会是空的,这个时候从节点如果同步主节点,会将之前的数据清洗掉,所以安全的做法是从节点先断开主从复制。
  • 一主多从:又被称为星状结构,适用于读写分离的场景。建立复制连接之后,除了一开始的数据同步,在后期还会进行实时的写命令同步,这样如果从节点过多,并且写并发量比较大的话,那么会造成主节点的性能严重降低,还会增加网络带宽。
  • 树状结构:可以理解为是对星状结构的优化,可以有效降低主节点负载,并且减少主节点同步的数目。

建立复制流程

  1. 保存主节点信息
    执行salveof命令后从节点不会里面同步数据,只会先保存主节点的信息。从节点会在后台维护一个定时任务,正在的连接会交由定时任务执行,这个时候在从节点执行info replication命令可以查看到复制任务的进展。
  2. 主从建立socket连接
    在发现有新节点接入时,从节点会通过内部维护的每秒定时任务建立一个socket连接,如果没有第一次没有建立成功,后面会继续无限次重试,直到建立连接成功或者使用salve no one命令取消复制。
  3. 发送ping命令
    建立连接成功之后从节点会发送ping命令用来检测是否建立的套接字是否可用以及主节点是否可以接受处理命令,如果中间没有接收到主节点响应的pong命令,那么会断开复制,在交由定时任务后面重试连接。
  4. 权限验证
    在校验完连接之后,如果主检点设置了requirepass配置,那么会对权限进行校验,这个时候从节点需要配置masterauth配置校验信息。如果没有通过校验也会断开复制,后面再继续重新发起复制流程。
  5. 同步数据集
    权限校验完之后,会同步主节点的数据,redis 2.8版本之前会全部一次性同步所有数据,在2.8之后psync命令同步数据,这个新命令会带有一种类似断点续传的功能,毕竟数据量大的情况下,全部重传会非常消耗性能。
  6. 命令持续复制
    数据同步完之后,主节点还会把写命令持续同步给所有节点(这里会有网络消耗,所以在星状结构中从节点过多,会降低主节点性能),保证主从的数据一致性。

数据同步

在redis2.8之前使用的sync同步命令是将所有数据全部复制,如果之前复制到一半,后期重新连接上开始复制,又会重新复制所有数据,其实可以不用这样,因为之前已经复制了一部分,所以重试连接后的复制只要从断开的部分开始就行了。这就是psync新命令的效果。

  1. 全量复制
    一般发生在第一次复制连接,早期的版本全部是全量复制,这样操作在数据量大并且发生网络闪断时会严重拉低主节点的性能。
  2. 部分复制
    2.8版本后期添加的,相对于全量复制,部分复制在发生网络断开重连后不会复制全部数据,会接着没有复制完的数据继续复制任务。

psync命令

在数据同步的时候经常会遇到需要查看复制信息以及进度的情况,这个时候可以使用info replication命令查看详情。还有一种情况就是关于runId的,当遇到需要调优的时候,会修改一些内存相关的配置,这时如果重启了redis服务,那么runId就会改变,所以要考虑到在不关服务的情况下重启,可以使用debug reload命令达到效果。

版本差异

redis在2.8之前使用的sync命令,全部全量复制,2.8~4.0的时候使用的是psync1命令,这个版本加上了部分复制,在4.0版本的时候使用了psync2命令,增加了重启部分复制功能以及混合持久化等。psync2命令后面找到资料后在继续记录,这里的psync1和psync2中的1和2都是版本,真实的命令还是psync。

相关知识点

  1. 主从节点各自复制偏移量
    主节点会将自身同步的数据量记录下来,从节点也会维护一个自身已经接收成功的数据量,并且还会每秒向主节点同步一次数据,主节点也需要记录下来,可以根据主节点记录的从节点偏移量以及从节点自身记录的偏移量判断同步的数据是否一致。
  2. 主节点复制积压缓冲区
    在主节点进行数据同步的时候,需要接收客户端的写命令,写命令除了同步给从节点以外还会写入到复制积压缓冲区中,主要用来做写命令的备份。在从节点出现网络断开重连时进行数据修补。
  3. 主节点运行id
    一个40位的16进制字符串,用来标志唯一的运行时redis服务,注意是运行时,如果redis重启之后id会改变,从节点会从新进行全量复制。查看runid的命令,info server

如果使用ip:port标志唯一的redis服务,如果当前redis重启之后并且主节点的数据集已经更改,那么再从之前的位置复制的话,主从数据就不一致了。

psync流程

前期

  1. 从节点发送psync命令到主节点,runid是主节点的,在建立复制关系时保存下来的。offset是偏移量,如果是第一次复制,则为-1,否则是从节点保存的复制偏移量。
  2. 主节点根据psync的两个参数作出相应,如果runid不一致,则直接进行全量复制,否则会进行部分复制。
  • 全量复制户响应:+FULLRESYNC
  • 部分复制:+CONTINUE
  • 错误响应:+ERR,无法识别psync命令,从节点将发送旧版本的sync命令触发全量复制

全量复制

当从节点收到来自主节点的全量复制的响应时,会进行全量复制。

  1. 1)、2)步骤是psync数据同步前期,这个阶段是来判断进行全量复制还是部分复制
  2. 3)从节点接受到来自主节点的全量复制响应,会保存主节点的runId以及offset
  3. 4)主节点开始执行bgsave命令,这个bgsave会引起RDB方式的持久化,持久化完成之后会发送给从节点
  4. 5)主节点将完成的RDB文件发送给从节点
  5. 6)发送完RDB文件之后,还会将复制积压缓冲区中的写命令同步给从节点。
  6. 7)从节点清空自身的数据
  7. 8)加载接收到的RDB文件。
  8. 9)从节点加载完RDB文件之后,如果当前节点开启的AOF持久化,那么会继续执行bgrewriteaof命令进行aof持久化。

全量复制问题思考

  1. 主节点数据量过大,同步时间过长,导致全量同步失败怎么办?
    在主节点同步的过程中如果RDB文件太大(G级别),repl-timeout决定复制的时间,如果超过这个时间则全量复制失败。
  2. 什么是无盘复制?
    为了降低主节点的磁盘写入开销,可以将redis生成的RDB文件不保存在磁盘中,直接发送给从节点,不过这个功能目前还不太完善,不建议线上使用,具体什么时候稳定,等待官方最新消息。
  3. 写命令高并发场景?
    在写命令比较频繁的场景:由于主节点在进行全量复制的时候依旧响应来自客户端的写命令,如果这个时候复制积压缓冲区的溢出,则主节点会关闭客户端连接,全量同步失败,建议将clint-output-buffer-limit设置大一点。
  4. 读写分离场景发生全量复制怎么办?
    读写分离场景,如果出现了全量复制的时间过长,从节点正在响应客户端的读请求,但是由于同步未完成,这个时候拿到的数据可能是过期的甚至是错误的。关于这点,redis提供了一个slave-serve-stale-data参数,这个参数代表的意思就是在复制期间从节点是否可以进行读请求响应,如果业务对于数据的准确性要求过高,那么可以将这个参数设置为no,这个时候从节点对于info和slaveof命令之外其他命令只会响应SYNC with master in process信息。
  5. 全量复制期间有哪些比较耗时的操作?
    • 主节点bgsave
    • 传输RDB文件
    • 从节点清空阶段
    • 从节点加载RDB阶段
    • AOF重写阶段(如果从节点开启了AOF重写)

部分复制

从上面可以得知一个全量复制的成本是相当的高的,所以redis在2.8版本之后对齐做了优化,出现了部分辅助功能,就是百度、迅雷等常用软件的一种断点续传的功能。在进行全量复制的过程中如果出现了网络闪断或者命令丢失等情况时,从节点会要求主节点补发丢失的命令数据,如果主节点的复制积压缓冲区存在这部分数据则直接发送给从节点。

  1. 1)主从之间网络出现中断
  2. 2)主节点依旧响应请求,这时的写命令会写入到复制积压缓冲区中
  3. 3)当主节点网络恢复后,从节点会在再次连上主节点
  4. 4)主从连接成功后,从节点会将断开之前自身保存的复制偏移量以及runId结合psync命令发送给主节点。
  5. 5)主节点首先判断runId是否一致,不一致的话直接进行全量复制,如果一致,再判断偏移量是否在复制缓冲区内,如果存在才会进行部分复制,此时会向从节点响应CONTINNUE命令。
  6. 6)主节点会根据偏移量向从节点发送部分数据

这里在看《redis开发与运维》这本书的时候,对于复制积压缓冲区还是不太明白,它到底是用来存储什么的,一开始我理解的是复制连接阶段的主节点写入命令,但是在部分复制章节看到,主节点会根据从节点的偏移量是否在复制积压缓冲区内来决定是否部分复制。那么就有个问题如果全量复制的时候发生网络中断了,那么从节点再连上之后是进行部分复制吗?如果是部分复制那之前的数据肯定不在缓冲区中啊!还是说部分复制只会发生在全量复制完成之后的时间段,如果连接断开则会丢失一段时间的写命令数据,这个时候只要把写命令数据同步了,就算部分复制了?这点有待深入了解后期跟新。(后面全量复制场景已经得到找到问题的答案了😅)

心跳

redis通过心跳维护主从节点之间的长连接,主节点默认每10秒(repl-ping-slave-period控制频率)发送ping命令,判断从节点的存活性和连接状态。从节点在主线程中每隔1秒发送replconf ack offset命令,给主节点上报当前的偏移量。

考点

  1. 心跳机制中从节点每次上报自身的偏移量有什么作用?
  • 实时监测主从节点网络状态,上报自身复制偏移量,检查复制数据是否丢失,如果从节点数据丢失,再从主节点的缓冲区中拉取数据,保证数据一致性。(如果偏移量不在复制积压缓冲区范围内怎么办?)
  • 实现保证从节点的数量和延迟性功能(min-slaves-to-write、min-slaves-max-lag)。

全量复制场景

  1. 第一次建立复制关系(无法避免)
  2. 运行id不一致

由于主节点故障重启导致的runId不一致,建议在主节点故障时自动将从节点晋升主节点

  1. 复制积压缓冲区内存不足

主从节点的网络发生闪断,当从节点重连成功后就会进行部分复制,但是如果在写并发量特别大或者重连时间过长导致复制积压缓冲区内存不足,那么这个时候主节点根据从节点发送来的偏移量不在缓冲区范围内,这个时候就无法进行部分复制,会退化成全量复制,这个时候就需要对缓冲区的大小进行合适的设置。当然在第一个全量的复制的时候如果复制时间特别长也导致缓冲区不足那么复制也会中断。

复制风暴

当一个主节点有多个从节点(星状拓扑),如果一旦主节点故障,重启成功后,那么会有多个从节点进行全量复制,这个会导致主节点发送RDB文件到多个从节点,严重降低主节点的性能,建议使用树状结构代替星状,减去主节点负担。还有个场景就是多个主节点部署在同一台机器上,这样也会造成单机复制风暴,就是主节点所在的服务器出现性能瓶颈,对于这种场景建议使用多机部署方案提高性能。

总结

关于复制主要了解一下复制的每个步骤的具体操作,以及全量复制和部分复制相关的内容

参考文档

《Redis开发与运维》