目录
- 主从复制的概念
- 旧版主从复制
- 同步
- 命令传播
- 旧版复制功能的缺陷
- 新版主从复制
- 部分重同步的实现
- 复制偏移量
- 复制积压缓冲区
- 服务器运行ID
- PSYNC命令实现
- 复制的实现
主从复制的概念
主从复制就是将一台Redis服务器上面的数据,复制到其他的Redis服务器,前者称为Redis主节点(master)后者称为从节点(salve)。且数据的复制是单向的,只能是从主节点到从节点。Redis支持主从同步和丛丛同步,丛丛同步主要是为了解决主节点的同步负担
主从复制的主要作用
- 实现数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式
- 故障恢复: 当主节点出现问题的时候,可以由从节点提供服务,实现快速的故障恢复。(实际上是一种服务冗余)
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载。尤其是在写少读多的场景下,通过多个从服务器分担负载,可以大大提高Redis服务器的并发量。
- 高可用的基石:主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
在Redis当中可以通过执行SLAVEOF命令或者设置slaveof 选项,让一个服务器去复制另一个服务器。
可以通过 salve of no one 断开复制
主从复制的开启,完全是在从节点发起的,不需要我们在主节点做任何事情。
可以通过 info replication查看主从复制状态。
旧版主从复制
Redis的复制功能分为 同步 和 命令传播 两个操作
- 同步(sync):将从服务器的数据库状态更新至主服务器当前所处的数据库状态
- 命令传播 :命令传播则用于在主服务的数据库状态被修改,导致主从服务器的数据库状态出现不一致的时候,让主从服务器的数据库重新回到一致状态。
同步
旧版的同步是需要通过向主服务发送SYNC命令来完成的,执行步骤如下:
- 从服务器向主服务器发送SYNC命令
- 收到SYNC命令的主服务器执行BGSAVE命令,在后台生成一个RDB文件,并使用一个缓冲区记录从现在开始的所有的写命令
- 当主服务的BGSAVE命令执行完毕之后,主服务器会将BGSAVE命令生成的RDB文件发送给从服务器,从服务器接受并载入这个RDB文件,将自己的数据库状态更新至主服务器执行BGSAVE命令时的数据库状态
- 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态。
命令传播
在同步操作完成之后,主从服务器的状态达到一致,但是这种一致并不是一成不变的,每当主服务器执行客户端发送的写命令的时候,主服务器的数据库就有可能会被修改,导致主从服务器状态不再一致
为了让主从服务器再次回到一致的状态,主服务器需要对从服务器执行命令传播的操作:主服务器会将自己执行的写命令,也就是造成主从服务器不一致的那一条命令,发送给从服务器执行,当从服务器执行了相同的写命令之后,主从服务器再次回到了一致的状态。
旧版复制功能的缺陷
在Redis当中,从服务器对主服务的复制可以分为两种情况
- 初次复制 : 从服务器以前没有父之过任何主服务器,或者从服务器当前要复制的主服务器和上一次复制的主服务器不同
- 断线后复制: 处于命令传播节点的主从服务器因为网络的原因中断了复制,但从服务器通过自动重连机制重连上了主服务器,并继续复制住服务器。
对于初次复制,旧版的复制功能已经能够很好的完成了任务。
但是对于断线后复制来说,旧版复制功能依然是执行SYNC命令,重新去复制主服务器上所有的数据,而不是去补足断线之后的那一部分数据。
SYNC命令式一个非常耗费资源的操作 每次执行SYNC命令,主从服务器需要执行以下动作:
- 主服务器需要执行BGSAVE命令来生成RBD文件,这个生成操作会耗费主服务大量的CPU、内存和磁盘I/O资源
- 主服务需要将自己的RDB文件发送给从服务器,这个发送的操作会耗费主从服务器大量的网络资源,并对主服务响应命令请求的时间造成影响
- 接收到RBD文件的从服务器需要载入主服务器发来的RDB文件,并且在载入期间,从服务器会因为阻塞而无法处理命令请求
特别是当出现断线重复制的情况,为了让从服务器不足断线后缺失的那一小部分数据,却要执行一次如此消耗资源的SYNC命令,显然是不合理的。
新版主从复制
Redis2.8版本开始,使用PSYNC命令来代替SYNC命令来执行复制时的同步操作:
PSYNC命令具有完整重同步和部分重同步的两种模式
- 完整重同步 : 和SYNC命令基本一样
- 部分重同步 : 用于处理断线后复制的情况,只复制断线后的那一部分数据 (+continue)
部分重同步的实现
部分重同步功能主要由以下三个部分构成:
- 主服务器的复制偏移量(replication offset)和从服务器的复制偏移量
- 主服务的复制积压缓冲区(replication backlog)
- 服务器的运行ID (run ID)
复制偏移量
执行复制的双方 : 主服务器和从服务器会分别维护一个复制偏移量
- 主服务器每次向从服务器传播N个字节的数据的时候,就将自己的复制偏移量的值加上N
- 从服务器每次收到主服务器传播N个字节的数据的时,就将自己的复制偏移量的值加上N
通过对比主从服务器的复制偏移量,就可以知道主从服务器是否处于一致状态:如果处于一致,那么偏移量应该是相同的。
如图此时从服务器A断线之后重连 发现自己的偏移量和主服务器的偏移量不一致,会像主服务器发送psync命令,服务器如何判断是该进行完整重同步还是部分重同步呢,就会与复制积压缓冲区有关。
复制积压缓冲区
复制积压缓冲区是由主服务器维护的一个固定长度的先进先出的队列,默认大小为1MB。
主服务器在进行命令传播的时候,他不仅会将写命令发送给所有的从服务器,还会将写命令入队到复制积压缓冲区里面。
因此。主服务器的复制积压缓冲区里面会保存着一部分最近传播的写命令,并且复制挤压缓冲区会为队列中的每个字节记录相应的复制偏移量。
当从服务器重现连上主服务器的时候,**从服务器会通过PSYNC命令将自己的复制偏移量发送给主服务器,**主服务器会根据这个复制偏移量来决定对从服务器执行何种同步操作:
- 如果offset偏移量之后的数据还在复制挤压缓冲区里面,那么主服务器对从服务器回送continue指令,并执行部分重同步,
- 否则 执行完整重同步。
服务器运行ID
为了实现部分重同步,还需要用到服务器运行ID
每个Redis服务器,不论是主服务器还是从服务器,都已有自己的运行ID
运行ID在服务器启动的时候自动生成。
当从服务器对主服务器进行初次复制的时候,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来。
当从服务器断线并重新连接上一个主服务器的时候,从服务器将向当前连接的主服务器发送之前保存的运行ID:
- 如果从服务器之前保存的运行ID和当前连接的主服务器的运行ID相同,那么说明之前复制的主服务器就是当前的主服务器,可以执行部分重同步
- 如果不同,执行完整的重同步。
PSYNC命令实现
复制的实现
- 设置主服务器的地址和端口
- 建立socket连接
- 从服务器向主服务器发送ping命令
- 身份验证
- 从服务器向主服务发送端口信息
- 同步,发送PSYNC命令
- 命令传播
- 心跳检测
身份验证问题:
salveof这个命令居然不需要验证? 这意味着只要知道了ip和端口号就可以随便拷贝服务器上的数据?
当然不是,可以在主节点配置 requirepass来设置密码,这样就必须在从节点中对应配置好 masterauth参数(与主节点的requirepass保持一致)才能够进行正常的复制