1,前言
读《Redis设计与实现》所作笔记
在Redis
中,用户可以通过SLAVEOF
命令或者设置slaveof
选项,让一个服务器去复制另一个服务器,我们称呼被复制的服务器为主服务器,而对主服务器进行复制的服务器则被称为从服务器
假设有两个服务器
- 服务器A,地址为
127.0.0.1:6379
- 服务器B,地址为
127.0.0.1:12345
- 在服务器A发送以下命令:
SLAVEOF 127.0.0.1:12345
那么服务器A就会成为服务器B的从服务器,服务器B也就是服务器A的主服务器
进行复制的主从服务器双方的数据库将保存相同的数据,概念上将这种现象称作:”数据库状态一致“,或者简称”一致“
主从复制功能以Redis2.8
版本为分界线,2.8版本以前的旧版复制功能在处理断线后重新连接的从服务器存在低效的问题;而Redis2.8
之后的新版复制功能则是通过部分重同步来解决旧版复制功能的低效问题
2,旧版复制功能的实现
Redis
的复制功能分为:
- 同步(
sync
):同步操作用于将从服务器的数据库状态更新至主服务器当前所处的数据库状态 - 命令传播(
command propagate
):命令传播操作用于在主服务器的数据库状态被修改,导致主从服务器的数据库状态不一致时,让主从服务器的数据库重新回到一致状态
2.1,同步
当客户端向从服务器发送了SLAVEOF
命令,要求从服务器复制主服务器时,从服务器首先需要执行同步操作,也即是:将从服务器的数据库状态更新至主服务器当前所处的数据库状态
从服务器通过向主服务器发送SYNC
命令完成同步,以下是SYNC
命令的执行步骤:
- 从服务器向主服务器发送
SYNC
命令 - 收到
SYNC
命令的主服务器执行BGSAVE
命令,在后台生成一个RDB
文件,并使用一个缓冲区记录从现在开始执行的所有写命令 - 当主服务器的
BGSAVE
命令执行完毕时,主服务器会将BGSAVE
命令生成的RDB
文件发送给服务器,从服务器接收并载入这个RDB
文件,将自己的数据库状态更新至主服务器执行BGSAVE
命令时的数据库状态 - 主服务器将记录在缓冲区里面的所有写命令发送给从服务器,从服务器执行这些写命令,将自己的数据库状态更新至主服务器数据库当前所处的状态
下表展示了一个主从服务器进行同步的例子:
时间 | 主服务器 | 从服务器 |
---|---|---|
T0 | 服务器启动 | 服务器启动 |
T1 | 执行SET k1 v1 | |
T2 | 执行SET k2 v2 | |
T3 | 执行SET k3 v3 | |
T4 | 向主服务器发送SYNC 命令 | |
T5 | 接收到从服务器发送来的SYNC 命令,执行BGSAVE 命令,创建包含键k1、k2、k3 的RDB 文件,并使用缓冲区记录接下来执行的所有写命令 | |
T6 | 执行SET k4 v4 ,并将这个命令记录到缓冲区里面 | |
T7 | 执行SET k5 v5 ,并将这个命令记录到缓冲区里面 | |
T8 | BGSAVE 命令执行完毕,向从服务器发送RDB 文件 | |
T9 | 接收并载入主服务器发来的RDB 文件,获得k1、k2、k3 三个键 | |
T10 | 向从服务器发送缓冲区中保存的写命令SET k4 v4 和SET k5 v5 | |
T11 | 接收并执行主服务器发来的两个SET 命令,得到k4 和k5 两个键 | |
T12 | 同步完成,现在主从服务器两者的数据库都包含了键k1、k2、k3、k4 和k5 | 同步完成,现在主从服务器两者的数据库都包含了键k1、k2、k3、k4 和k5 |
2.2,命令传播
在同步操作执行完毕之后,主从服务器两者的数据库将达到一致状态,但这种一致并不是一成不变的。当主服务器执行写命令之后,主从服务器之间的数据库状态有可能不再一致,这时候就需要主服务器将刚刚执行的写命令传递给从服务器,让从服务器执行这条写命令,从而使得主从服务器数据库状态重新达到一致。
2.3,缺陷
在Redis
中,从服务器对主服务器的复制可以分为以下两种情况:
- 初次复制:从服务器以前没有复制过任何主服务器,或者从服务器当前要复制的主服务器和上一次复制的主服务器不同【没有复制过主服务器或者换主服务器复制了】
- 断线后重复制,处于命令传播阶段的主从服务器因为网络原因中断复制,但从服务器通过自动重连重新连接上了主服务器,并继续复制主服务器【连的好好的,突然断开连接,然后重新连接,导致主从服务器数据库状态有可能不一致】
对于初次复制,旧版的复制功能能很好的完成任务,但对于断线后重复制来说,旧版复制功能虽然能让主从服务器重新回到一致状态,但效率很低
具体情况以下的示例:
时间 | 主服务器 | 从服务器 |
---|---|---|
T0 | 主从服务器完成同步 | 主从服务器完成同步 |
T1 | 执行并传播SET k1 v1 | 执行主服务器传来的SET k1 v1 |
T2 | 执行并传播SET k2 v2 | 执行主服务器传来的SET k2 v2 |
…… | …… | …… |
T10085 | 执行并传播SET k10085 v10085 | 执行主服务器传来的SET k10085 v10085 |
T10086 | 执行并传播SET k10086 v10086 | 执行主服务器传来的SET k10086 v10086 |
T10087 | 主从服务器连接断开 | 主从服务器连接断开 |
T10088 | 执行并传播SET k10087 v10087 | 断线中,尝试重新连接主服务器 |
T10089 | 执行并传播SET k10088 v10088 | 断线中,尝试重新连接主服务器 |
T10090 | 执行并传播SET k10089 v10089 | 断线中,尝试重新连接主服务器 |
T10091 | 主从服务器重新连接 | 主从服务器重新连接 |
T10092 | 向主服务器发送SYNC 命令 | |
T10093 | 接收到服务器发来的SYNC 命令,执行BGSAVE 命令,创建包含键k1 至键10089 的RDB 文件,并使用缓冲区记录接下来执行的所有写命令 | |
T10094 | BGSAVE 命令执行完毕,向从服务器发送RDB 文件 | |
T10095 | 接收并载入主服务器发来的RDB文件,获得键k1 至k10089 | |
T10096 | 因为在BGSAVE 命令执行期间,主服务器都没有执行任何写命令,所以跳过发送缓冲区包含的写命令这一步 | |
T10097 | 主从服务器再次完成同步 | 主从服务器再次完成同步 |
在时间T10091,从服务器重新连接到了主服务器,因为这时主从服务器状态已经不一致了,所以从服务器将向主服务器发送SYNC
命令,而主服务器将包含键k1至键k10089的RDB
文件发送给从服务器,从服务器通过接收和载入这个RDB
文件来将自己的数据库更新至主服务器数据库当前所处的状态
虽然SYNC
命令能让主从服务器重新回到一致状态,但仔细研究这个断线重复制过程,可以发现传送RDB
文件这一步并非必须的:
- 主从服务器之间的数据库状态不同是在于键k10087至键k10089,而键k1至键k10086这些键是一样的,从服务器只需要添加键k10087至键k10089即可到达主从服务器数据库状态一致
- 可惜的是,旧版复制功能并没有根据上面的情况提出解决办法,而是让主服务器生成并向从服务器发送包含键k1至键k10089的RDB文件,但实际上
RDB
文件中包含的键k1至键k10086对于从服务器来说都是不必要的
上述的情况可能有一点理想化:而在主从服务器断线期间,主服务器执行的写命令可能会有成百上千之多,不仅仅是两三个命令,并且断线期间执行的写命令所产生的数据量要比数据库中的数据量少得多。在这种情况下,为了让从服务器补足一小部分缺失的数据,却要让主从服务器重新执行一次SYNC
命令,重新载入RDB
文件,这种做法无疑是非常低效的
SYNC命令是一个非常耗费资源的操作
每次执行SYNC
命令,主从服务器都需要执行以下动作:
- 主服务器需要执行
BGSAVE
命令来生成RDB
文件,这个生成操作会耗费主服务器大量的CPU
、内存和磁盘I/O
资源 - 主服务器需要将自己生成的
RDB
文件发送给从服务器,这个发送操作会耗费主从服务器大量的网络资源(带宽和流量),并对主服务器响应命令请求的时间产生影响 - 接收到
RDB
文件的从服务器需要载入服务器发来的RDB
文件,并且在载入期间,从服务器会因为阻塞而没办法处理命令请求
因为SYNC
命令是一个耗费资源的操作,所以Redis
必须慎之又慎的使用SYNC
命令,只有在真正有需要的时候才执行
3,新版复制功能
为了解决旧版复制功能在断线重复制情况下的低效问题,Redis
从2.8开始,使用PSYNC
命令代替SYNC
命令来执行复制时的同步操作
PSYNC
命令具有完整重同步和部分重同步两种模式:
- 完整重同步:完整重同步的执行步骤和
SYNC
命令的执行步骤基本一样,都是通过主服务器创建RDB
文件,并及时推送主服务器保存在缓冲区中的写命令来进行同步 - 部分重同步:部分重同步则用于处理断线后重复制的情况,当从服务器断线后重新连接主服务器。如果条件允许,主服务器可以将断线期间执行的命令发送给从服务器,从服务器只需要接受并执行这些写命令,这样就可以将数据库更新至主服务器当前所处的状态
看以下的示例:
时间 | 主服务器 | 从服务器 |
---|---|---|
T0 | 主从服务器完成同步 | 主从服务器完成同步 |
T1 | 执行并传播SET k1 v1 | 执行主服务器传来的SET k1 v1 |
T2 | 执行并传播SET k2 v2 | 执行主服务器传来的SET k2 v2 |
…… | …… | …… |
T10085 | 执行并传播SET k10085 v10085 | 执行主服务器传来的SET k10085 v10085 |
T10086 | 执行并传播SET k10086 v10086 | 执行主服务器传来的SET k10086 v10086 |
T10087 | 主从服务器连接断开 | 主从服务器连接断开 |
T10088 | 执行并传播SET k10087 v10087 | 断线中,尝试重新连接主服务器 |
T10089 | 执行并传播SET k10088 v10088 | 断线中,尝试重新连接主服务器 |
T10090 | 执行并传播SET k10089 v10089 | 断线中,尝试重新连接主服务器 |
T10091 | 主从服务器重新连接 | 主从服务器重新连接 |
T10092 | 向主服务器发送PSYNC 命令 | |
T10093 | 向从服务器返回+CONTINUE 回复,表示执行部分重同步 | |
T10094 | 接收+CONTINUE 回复,准备执行部分重同步 | |
T10095 | 向从服务器发送SET k10087 v10087、SET k10088 v10088、SET k10089 v10089 | |
T10096 | 接受并执行主服务器传来的三个SET 命令 | |
T10097 | 主从服务器再次完成同步 | 主从服务器再次完成同步 |
相比于SYNC
命令,PSYNC
命令的执行只需要将从服务器缺少的命令发给从服务器执行即可。
那么接下来的问题就是,主服务器如何确定从服务器缺少哪些命令?
部分重同步的实现
部分重同步功能由以下三个部分构成:
- 主服务器的复制偏移量(
replication offset
)和从服务器的复制偏移量 - 主服务器的复制积压缓冲区(
replication backlog
) - 服务器的运行
ID
(run ID
)
3.1,复制偏移量
执行复制的双方都会维护一个复制偏移量
- 主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N
- 从服务器每次收到主服务器传播来的N个字节的数据,就将自己的复制偏移量的值加上N
假如有这么几部服务器:
如果这时主服务器向三个从服务器传播长度为33字节的数据,那么主服务器的复制偏移量将更新为10119,而三个从服务器接收到主服务器传播的数据之后,也会将复制偏移量更新为10119:
假设从服务器A断线重新连接到主服务器之后,从服务器将向主服务器发送PSYNC
命令,报告从服务器A当前的复制偏移量为10086,那么主服务器对从服务器执行完整重同步还是部分重同步?如果是部分重同步,又该怎么补偿给从服务器确实的数据?答案与复制积压缓冲区有关
3.2,复制积压缓冲区
复制积压缓冲区是由主服务器维护的一个固定长度先进先出队列,默认大小为1MB
固定长度先进先出队列的入队和出队规则跟普通的先进先出队列一样,不同的是:普通先进先出队列随着元素的增加和减少会动态调整长度,而固定长度先进先出队列的长度是固定的,当入队元素的数量大于队列长度时,最先入队的元素会被弹出,而新元素会被放入队列
例如一个长度为3的固定长度先进先出队列,现在要将‘h’、’e’、‘l’、‘l’、‘o’
五个字符放进队列中
- 先放入
‘h’、’e’、‘l’
,此时队列满了 - 此时再放入
‘l’
,会将‘h’弹出队列,此时队列会变成:‘e’、’l’、‘l’
- 接着放入
‘o’
复制积压缓冲区大小的确定
【值得一提的是:如果复制积压缓冲区的大小设置不合理,那么PSYNC
的复制重同步模式就不能正常发挥作用,因此正确估算和设置复制积压缓冲区的大小非常重要。复制积压缓冲区的最小大小的确定可以根据公式 (从服务器断线后重新连接上主服务器所需的平均时间)* (主服务器平均每秒产生的写命令数据量(协议格式的写命令的长度总和))】
当主服务器进行命令传播时,它不仅会将写命令发送给所有从服务器,还会将写命令入队到复制积压缓冲区中:
因此主服务器的复制积压缓冲区里面会保存着一部分最近传播的写命令,并且复制积压缓冲区会为队列中的每个字节记录相应的复制偏移量,如:
当从服务器重新连上主服务器,从服务器会通过PSYNC命令将自己的复制偏移量offset
发送给主服务器,主服务器会根据这个复制偏移量决定对从服务器执行完整重同步还是部分重同步:
- 如果
offset
偏移量之后的数据(也即是偏移量offset+1
开始的数据)仍然存在于复制积压缓冲区,那么主服务器将对从服务器执行部分重同步操作 - 相反,如果
offset
偏移量之后的数据已经不存在于复制积压缓冲区,那么主服务器将对从服务器执行完整重同步操作
结合之前的例子,新版复制功能处理断线重连的例子:
- 当从服务器A断线之后,它立即重新连接主服务器,并向主服务器发送
PSYNC
命令,报告自己的复制偏移量为10086 - 主服务器接收到从服务器发来的
PSYNC
命令以及偏移量10086,主服务器将检查偏移量10086之后的数据是否存在于复制积压缓冲区里面,结果发现这些数据仍然存在,那么主服务器会向从服务器发送+CONTINUE
回复,表示数据同步将以部分重同步来进行 - 接着主服务器会将复制积压缓冲区10086偏移量之后的所有数据都发送给从服务器
- 从服务器只需要接收这33字节的缺失数据,就可以与主服务器数据库状态重新保持一致!
3.3,服务器运行ID
除了复制偏移量和复制积压缓冲区之外,部分重同步还需要用到服务器运行ID
(run ID
)
- 每个
Redis
服务器,不论主服务器还是从服务器都有一个自己的运行ID
- 运行
ID
在服务器启动时自动生成,由40个随机的十六进制字符组成
当从服务器对主服务器进行初次复制时,主服务器会将自己的运行ID传送给从服务器,而从服务器则会将这个运行ID保存起来。当从服务器断线并重新连上一个主服务器时,从服务器将向当前连接的主服务器发送之前保存的运行ID
:
- 如果发送的运行
ID
和当前主服务器的运行ID
相同,那么代表从服务器断线之前连接的正是这个主服务器,主服务器将会尝试执行部分重同步操作 - 如果不相同,那么代表从服务器断线前连接的不是当前的主服务器,主服务器将会对从服务器进行完整重同步操作
4,PSYNC命令的实现
PSYNC
命令的调用方式有两种:
- 如果从服务器以前没有复制过任何主服务器,或者之前执行过
SLAVEOF no one
命令,那么从服务器在开始一次新的复制时将向主服务器发送PSYNC ? -1
命令,主动请求主服务器进行完整重同步 - 相反,如果从服务器已经复制过某个主服务器,那么从服务器在开始一次新的复制时将向主服务器发送
PSYNC <runid> <offset>
命令,其中runid
是上一次复制的主服务器的运行ID
,而offset
则是从服务器当前的复制偏移量,接收到这个命令的主服务器会通过这两个参数判断应该对服务器进行哪种同步操作
根据情况,接收到PSYNC
的主服务器会向从服务器返回下面三种回复之一:
- 如果主服务器返回
+FULLREYSNC <runid> <offset>
回复,表示主服务器将与从服务器执行完整重同步操作runid
:主服务器的运行ID
,从服务器会将这个ID
保存起来,在下次发送PSYNC
命令时使用offset
:主服务器当前的复制偏移量,从服务器会将这个值作为自己的初始化偏移量
- 如果主服务器返回
+CONTINUE
回复,那么表示主服务器将与从服务器执行部分重同步操作,从服务器只要等着主服务器将自己缺少的那部分数据发送过来就可以了 - 如果主服务器返回
-ERR
回复,表示主服务器的版本低于Redis2.8
,它识别不了PSYNC
命令,从服务器将向主服务器发送SYNC
命令,并与主服务器执行完整重同步操作
相关的示意图如下:
以下是一个完整的复制-》网络中断-重复制例子:
- 首先有主服务器A:
127.0.0.1:6379
和从服务器:127.0.0.1:12345
- 客户端向从服务器发送命令
SLAVEOF 127.0.0.1 6379
,假设从服务器是第一次执行复制操作,那么从服务器将向主服务器发送PSYNC ?-1
命令,请求主服务器执行完整重同步操作 - 主服务器收到完整重同步请求后,在后台执行
BGSAVE
命令,并向从服务器返回+FULLRESYNC 主服务器运行ID 10086
,其中的10086是主服务器复制偏移量 - 假设完整重同步成功执行,并且主从服务器在一段时间内保持数据库状态一致。但是在复制到偏移量为20000的时候,主从服务器之间的网络连接中断,一段时间后,从服务器重新连接至主服务器,并再次对主服务器进行复制
- 因为之前对主服务器进行过复制,所以从服务器将向主服务器发送命令
PSYNC 主服务器运行ID 20000
,请求进行部分重同步 - 主服务器接收到从服务器的
PSYNC
命令之后,首先对比从服务器传来的运行ID
,并和自身的运行ID
进行比较。结果显示相同,于是主服务器继续读取从服务器传来的偏移量20000,检查偏移量为20000之后的数据是否存在于复制积压缓冲区中,结果发现仍然存在 - 确认运行
ID
相同并且数据存在之后,主服务器就会向服务器返回+CONTINUE
回复,表示将于从服务器执行部分重同步操作,之后主服务器将从服务器需要的所有数据(偏移量之后的数据)发送给从服务器,主从服务器数据库状态再次回到一致
5,复制的实现
以下是Redis2.8
及以上版本的复制功能的详细实现步骤:
- 设置主服务器的地址和端口
- 建立套接字连接
- 发送
PING
命令 - 身份验证
- 发送端口信息
- 同步
- 命令传播
5.1,设置主服务器的地址和端口
当客户端向从服务器执行以下命令:
127.0.0.1:12345> SLAVEOF 127.0.0.1 6379
OK
从服务器首先要做的就是将客户端给定的主服务器IP
地址和端口保存到服务器状态的masterhost
属性和masterport
属性里面:
struct redisServer{
//...
//主服务器地址
char *masterhost;
//主服务器端口
int masterport;
//...
};
执行完命令之后,从服务器的服务状态:
5.2,建立套接字连接
执行SLAVEOF
命令后,从服务器将根据命令设置的地址和端口,创建连向主服务器的套接字连接。如果从服务器创建的套接字能成功连接到主服务器,那么从服务器将为这个套接字关联一个专门用于处理复制工作的文件事件处理器,这个处理器将负责执行后续的复制工作。比如接收RDB
文件、接收主服务器传播过来的写命令等。
主服务器在接收从服务器的套接字连接之后,将为该套接字创建相应的客户端状态,并将从服务器看作是一个连接到主服务器的客户端来对待。【这时从服务器将具有服务器与客户端两个身份】
因为复制工作接下来的几个步骤都会以从服务器向主服务器发送命令请求的形式来进行,所以理解“从服务器是主服务器的客户端”这点非常重要
5.3,发送PING命令
当从服务器成为主服务器的客户端之后,第一件事就是向主服务器发送一个PING
命令
此处的PING
命令有两个作用:
- 虽然主从服务器成功建立起套接字连接,但双方并未使用该套接字进行过任何通信,通过发送
PING
命令可以检查套接字读写状态是否正常 - 通过发送
PING
命令查看主服务器是否能正常处理命令请求
从服务器在发送PING
命令之后将遇到以下三种情况的其中一种:
- 主服务器向从服务器发送了命令回复,但由于主从服务器之间的网络连接不稳定,不能继续执行复制工作的后续步骤。当出现这种情况,从服务器断开并重新创建连向主服务器的套接字
- 如果主服务器向从服务器返回一个错误,那么表示主服务器暂时没办法处理服务器的命令请求,不能继续执行复制工作的后续步骤。当出现这种情况,从服务器断开并重新创建连向主服务器的套接字
- 如果从服务器读到
PONG
回复,表示主从服务器之间网络连接正常并且主服务器能正常处理从服务器的命令请求。这种情况下,从服务器可以继续执行复制工作的下个步骤
5.4,身份验证
从服务器接收到主服务器的PONG
回复,下一步就是决定是否进行身份验证:
- 如果从服务器设置了
masterauth
选项,那么进行身份验证 - 如果从服务器没有设置
masterauth
选项,那么不进行身份验证
在需要进行身份验证的情况下,从服务器将向主服务器发送一条AUTH
命令,命令的参数为服务器,masterauth
选项的值。
假设从服务器的masterauth
的值为10086,那么从服务器将向主服务器发送AUTH 10086
从服务器在身份验证阶段可能遇到的情况:
- 主服务器没有设置
requirepass
选项,从服务器也没有设置masterauth
选项,那么无事发生,复制工作继续进行 - 主服务器设置了
requirepass
选项,从服务器没有设置masterauth
选项,那么主服务器将返回一个NOAUTH
错误;如果主服务器没有设置requirepass
选项,从服务器设置了masterauth
选项,那么主服务器将返回一个no password is set
错误 - 如果从服务器通过
AUTH
命令发送的密码和主服务器requirepass
选项的值一致,那么主服务器将继续执行从服务器发送的命令,复制工作继续进行;如果不相同,那么主服务器将返回一个invalid password
错误
所有的错误都会让从服务器停止当前复制工作,并从创建套接字开始重新执行复制,直到身份验证通过,或者从服务器放弃执行复制为止
5.5,发送端口信息
在身份验证步骤之后,从服务器将执行命令REPLCONF listening-port <port-number>
向主服务器发送从服务器的监听端口号
主服务器接收到这个命令之后,将端口号记录在从服务器所对应的客户端状态的slave_listening_port
属性中
5.6,同步
在这一步,从服务器将向主服务器发送PSYNC
命令,执行同步操作,有关PSYNC
的信息已经在上面讲述过了,所以不赘述
5.7,命令传播
当完成同步之后,主从服务器就会进入命令传播阶段,主服务器只要一直将自己执行的写命令发送给从服务器,而从服务器只需执行这些命令即可,就能保证主从服务器数据库状态一致
6,心跳检测
命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令REPLCONF ACK <replication_offset>
,其中replication_offset
是从服务器当前的复制偏移量。
REPLCONF ACK
命令对于主从服务器有三个作用:
- 检测主从服务器的网络连接状态
- 主从服务器之间通过发送和接收
REPLCONF ACK
命令来检查两者之间的网络连接是否正常:如果主服务器超过一秒钟没接收到从服务器发来的REPLCONF ACK
命令,那么主服务器就知道主从服务器之间的连接出现问题了
- 主从服务器之间通过发送和接收
- 辅助实现
min-slaves
选项Redis
的min-slaves-to-write
和min-slaves-max-lag
选项可以防止主服务器在不安全的情况下执行命令- 假设
min-slaves-to-wirte
的值为3,min-slaves-max-lag
的值为10。那么在从服务器的数量少于3个,或者三个从服务器的延迟(lag
)值都大于或等于10秒时,主服务器会拒绝执行写命令
- 检测命令丢失
- 如果因为网络故障,导致主服务器发送给从服务器的写命令没有被从服务器接收,那么当从服务器向主服务器发送
REPLCONF ACK
命令时,带上从服务器的复制偏移量,主服务器接受命令会与自己的复制偏移量进行比较,就能得知是否发生了命令丢失。然后主服务器就会根据服务器提交的复制偏移量,将复制积压缓冲区中找到从服务器缺失的数据发送给从服务器
- 如果因为网络故障,导致主服务器发送给从服务器的写命令没有被从服务器接收,那么当从服务器向主服务器发送
【值得一提的是:REPLCONF ACK
是Redis2.8
版本新增的,Redis2.8
以前的版本,即便发生了命令丢失,主从服务器都不会注意到,主服务器更不会向从服务器补发缺失的数据,所以为了保证复制时的数据一致性,最好使用Redis2.8
及以上版本】