《Redis设计与实现》读书笔记(二十五)
——Redis主从复制具体过程
(原创内容,转载请注明来源,谢谢)
一、PSYNC命令执行过程
psync是新版redis复制使用的命令,下面讨论其执行过程。
psync命令调用方法有两种:
1)从服务器以前如果没有复制过任何主服务器,或者执行过slaveof no one命令,则第一次复制会发送psync ? -1命令,表示强制要求主服务器进行完全重同步。
2)如果从服务器以前复制过主服务器,则会发送psync <runid> <offset>,将主服务器的运行ID和从服务器当前自身复制偏移量发送给主服务器,由主服务器判断是要部分重同步还是完全重同步。
主服务器的回复有三种:
1)+fullresync<runid> <offset>,则表示要完全重同步。从服务器会记录runid,作为下次断线重连发送的内容;将offset作为从服务器当前的偏移量。
2)+continue,表示部分重同步,从服务器只需要等待主服务器将缺少的部分发送过来,再进行同步即可。
3)-err,表示主服务器版本低于2.8,不支持psync,则从服务器会再发送sync命令,进行完整同步。
如下图所示:
二、复制的实现
复制采用的命令是slaveof<masterip> <masterport>,详细实现步骤如下:
1、设置主服务器的ip和端口
执行上述命令后,从服务器会将主服务器的ip和端口设置在自身redisServer结构体中的属性中,ip设置到char数组的masterhost名称,port设置到int的masterport。
设置完成后,从服务器会向客户端返回OK。
2、建立套接字连接
执行slaveof命令后,从服务器将根据ip和端口号,与主服务器建立套接字连接。
从服务器成功连接主服务器后,从服务器将为该连接专门创建一个用于复制工作的文件事件处理器,这个处理器接收rdb文件、处理主服务器的命令传播等。
主服务器接收到从服务器的套接字连接后,为套接字创建相应的状态,并将从服务器看作一个连接到主服务器的客户端,此时从服务器同时具有客户端和服务器两个身份,从服务器可以向主服务器发送命令请求,主服务器会向从服务器发送命令回复。
3、发送PING命令
从服务器连接上主服务器后,会先发送一个PING命令,具有两个作用:
1)检查主从服务器的套接字读写状态是否正常。
2)检查主服务器当前可以正常处理请求。
从服务器会收到主服务器的三种回复的一种:
1)主服务器向从服务器回复内容,从服务器不能在规定时限内读取命令内容,则认为主从服务器当前网络不佳,无法处理后续的同步工作。从服务器会断开套接字,再自动重连并重新创建向主服务器的套接字。
2)主服务器向从服务器回复一个错误,表示主服务器当前无法处理从服务器的请求,从服务器会断开并重新创建向主服务器的套接字。
3)从服务器接收到主服务器的PONG回复,表示主从服务器网络正常,主服务器当前可以处理从服务器的命令,则从服务器会进入后续的步骤。
PING命令判断流程如下:
4、身份验证
从服务器收到PONG后,将进入身份验证阶段。
如果其有设置masterauth选项,则开始身份验证,否则不验证。如果需要验证,则从服务器接下来会给主服务器发送auth命令,并带上masterauth选项的值作为参数。
身份验证阶段会有以下几个结果:
1)主服务器没有设置requirepass选项,从服务器也没有设置masterauth,则跳过身份验证。
2)主服务器没有设置requirepass,而从服务器设置masterauth,会报noauth错误。
3)主服务器设置requirepass,而从服务器没有设置masterauth,会报nopassword is set错误。
所有错误都会终止当前的复制工作,并从创建套接字开始重新执行复制,直到验证通过。
5、发送端口信息
身份验证通过后,从服务器会向主服务器发送replconf listening-port <port-number>命令,向从服务器监听端口号。
主服务器接收到端口号后,会将其记录在对应客户端redisClient结构体中的属性中,类型是int,名称是slave-listening-port。该属性目前唯一作用是主服务器执行info replication命令时,打印出从服务器的端口号。
6、同步
完成上述步骤后,从服务器会发送psync给主服务器。
在同步操作之前,只有从服务器是主服务器的客户端,而同步之后,主服务器也是从服务器的客户端,即互为客户端。
这样主服务器才可以将保存在缓冲区的写命令(完整重同步)、保存在复制积压缓冲区(部分重同步)中的写命令发给从服务器。
7、命令传播
完成同步后,后续只要主服务器状态改变,都会给从服务器发送写命令,从服务器执行写命令后,就保证主从一致性。
六、心跳检测
在命令传播阶段,从服务器默认每秒1次的频率,向主服务器发送如下命令:
replconfack <replication_offset>
replication_offset是从服务器的偏移量,发送此命令有三个作用:
1、检测主从服务器网络状态。
如果主服务器超过1秒没有接收到从服务器的replconfack命令,表示主从间的连接出问题了。info replication命令可以查看从服务器最后一次发送该命令的时间,属性值是lag,即lag大于1表示出问题了。
2、辅助实现min-slaves选项。
redis的min-slaves-to-write和min-slaves-max-lag两个选项可以在主服务器不安全的情况下,防止执行客户端的写命令。
例如min-slaves-to-write3,min-slaves-max-lag 10,表示少于3个从服务器或三个从服务器的延迟(lag)值都大于等于10秒,主服务器将拒绝执行写命令。
3、检测命令丢失。
由于每次发送replconfack命令,都带上当前的偏移量,这样就可以保证如果主服务器发送给从服务器的命令传播,由于网络问题导致数据丢失的问题能1秒内被主服务器发现。主服务器发现后,会将相应偏移量的内容从复制积压缓冲区取出,并发送给从服务器。
replconfack命令和复制积压缓冲区的概念,都是从redis2.8开始有的,在此之前的版本,主服务器无法发现从服务器的数据丢失。因为使用主从的情况下,为了数据一致性,尽量使用2.8以上版本的redis。
七、总结
1、redis2.8之前的版本,主从复制中,每次从服务器断线重连后,都要进行整个服务器的同步;而2.8版本开始,在一定情况下,可以实现部分重同步。
2、部分重同步是通过复制偏移量来判断是否需要,通过复制积压缓冲区来取出偏移量范围内的全部数据,通过从服务器发送给主服务器的主服务器运行ID来判断上一次从服务器连接的主服务器与此次连接是否一致。
3、如果复制偏移量超出复制积压缓冲区,或者从服务器发送的运行ID和主服务器当前运行ID不一致,或者主服务器的redis版本在2.8之前,都会进行完全重同步。
4、复制刚开始,从服务器是客户端,到开始同步的时候,主从服务器互为客户端。
5、复制过程包括:从服务器设置主服务器ip和端口;建立套接字;发送PING命令;身份验证;发送端口信息;同步;命令传播。
6、当主从进入命令传播阶段,从服务器每秒(时间可配置)给主服务器发送一条命令,内容是当前从服务器的偏移量,以确保主从网络正常,并且如果偏移量和主服务器当前的不一致,也便于主服务器再次发送偏移数据,以保证主从数据一致性。
7、由于效率以及数据一致性,如果要采用主从同步,建议使用redis2.8或以上的版本。
——written by linhxx 2017.09.12