vlambda博客
学习文章列表

杭州某小厂:Redis主从握手流程

你好,我是yes。(文末送 Redis 相关书籍 )

印象很深刻,之前我去面试就被一位老面试官问了:Redis主从复制机制,今天咱们就来分析分析!

下面详细分析Redis主从复制机制中主从握手的过程。

Redis主从复制机制中有两个角色:主节点与从节点。

主节点处理用户请求,并将数据复制给从节点。

主从复制机制主要有以下作用:

(1)数据冗余,将数据热备份到从节点,即使主节点由于磁盘损坏丢失数据,从节点依然保留数据副本。

(2)读/写分离,可以由主节点提供写服务,从节点提供读服务,提高Redis服务整体吞吐量。

(3)故障恢复,主节点故障下线后,可以手动将从节点切换为主节点,继续提供服务。

(4)高可用基础,主从复制机制是Sentinel和Cluster机制的基础,Sentinel和Cluster都实现了故障转移,即主节点故障停止后,Redis负责选择一个从节点切换为主节点,继续提供服务。

下面将主从复制流程分为三个阶段。

(2)同步阶段:从节点连接主节点后,需要先同步数据,数据达到一致(或者只有最新的变更不一致)后才进入复制阶段。

Redis支持两种同步机制:

  • 全量同步:从节点发送命令PSYNC ? -1,要求进行全量同步,主节点返回响应+FULLRESYNC,表明同意全量同步。随后,主节点生成RDB数据并发送给从节点。这种方式常用于新的从节点首次同步数据。

  • 部分同步:从节点发送命令PSYNC replid offset,要求进行部分同步,主节点响应+CONTINUE,表明同意部分同步。主节点只需要把复制积压区中offset偏移量之后的命令发送给从节点即可(主节点会将执行的写命令都写入复制积压区)。这种方式常用于主从连接断开重连时同步数据。如果offset不在复制积压区中,那么主节点也会返回+FULLRESYNC,要求进行全量同步。

(3)复制阶段:主节点在运行期间,将执行的写命令传播给从节点,从节点接收并执行这些命令,从而达到复制数据的效果。Redis使用的是异步复制,主节点传播命令后,并不会等待从节点返回ACK确认。异步复制的优点是低延迟和高性能,缺点是可能在短期内主从节点数据不一致。

本文中指的命令,包含命令名及执行命令的参数。

PSYNC命令涉及以下属性:

  • server.master_repl_offset:记录当前服务器已执行命令的偏移量。

  • server.replid:40位十六进制的随机字符串,在主节点中是自身ID,在从节点中记录的是主节点ID。

  • server.replid2:用于主节点,存放上一个主节点ID。

  • server.repl_backlog:复制积压区,主节点将最近执行的写命令写入复制积压区,用于实现部分同步。

下面介绍一下Redis主从握手流程。

主从复制的机制是由从节点发起流程,我们可以发送REPLICAOF命令到某个服务器,要求它成为指定服务器的从节点:

REPLICAOF <masterip> <masterport>

或者在配置文件中添加配置REPLICAOF <masterip> <masterport>,这样Redis服务器启动后将成为指定服务器的从节点。

提示:从Redis 5开始为SLAVEOF命令提供别名REPLICAOF,这两个命令的作用一样。

下面以从节点的视角,分析主从握手的过程。

从节点握手阶段涉及以下属性。

server.repl_state:用于从节点,标志从节点当前复制状态。有如下值:

  • REPL_STATE_NONE:无主从复制关系。

  • REPL_STATE_CONNECT:待连接。

  • REPL_STATE_CONNECTING:正在连接。

  • …(部分握手状态并没有列出)

  • REPL_STATE_TRANSFER:从节点正在接收RDB数据。

  • REPL_STATE_CONNECTED:已连接,主从同步完成。

从节点使用replicaofCommand函数处理REPLICAOF命令。

该函数执行如下逻辑:

(1)如果处理的命令是REPLICAOF NO ONE,则将当前服务器转换为主节点,取消原来的主从复制关系,退出函数。

(2)调用replicationSetMaster函数,与给定服务器建立主从复制关系。

另外,我们在配置文件中配置REPLICAOF <masterip> <masterport>,Redis加载该配置,也会将server.repl_state设置为REPL_STATE_CONNECT状态(config.c)。

从节点server.repl_state进入REPL_STATE_CONNECT状态后,主从复制流程已经开始。

serverCron时间事件负责对REPL_STATE_CONNECT状态进行处理:

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { ... if (server.repl_state == REPL_STATE_CONNECT) { if (connectWithMaster() == C_OK) { serverLog(LL_NOTICE,"MASTER <-> REPLICA sync started"); } }}

调用connectWithMaster函数进行处理,该函数负责建立主从网络连接:

int connectWithMaster(void) { // [1] server.repl_transfer_s = server.tls_replication ? connCreateTLS() : connCreateSocket(); // [2] if (connConnect(server.repl_transfer_s, server.masterhost, server.masterport, NET_FIRST_BIND_ADDR, syncWithMaster) == C_ERR) { ... return C_ERR; }

// [3] server.repl_transfer_lastio = server.unixtime; server.repl_state = REPL_STATE_CONNECTING; return C_OK;}

【1】创建一个Socket套接字。connCreateTLS函数创建TLS连接,connCreateSocket函数创建TCP连接,它们都返回套接字文件描述符。该连接是主从节点网络通信的连接,本书称之为主从连接。

【2】connConnect函数负责连接到主节点,并且在连接成功后调用syncWithMaster函数。

【3】从节点server.repl_state进入REPL_STATE_CONNECTING状态。

网络连接成功后,从节点调用syncWithMaster函数,进入握手阶段:

void syncWithMaster(connection *conn) { char tmpfile[256], *err = NULL; int dfd = -1, maxtries = 5; int psync_result; ... // [1] if (server.repl_state == REPL_STATE_CONNECTING) { connSetReadHandler(conn, syncWithMaster); connSetWriteHandler(conn, NULL); server.repl_state = REPL_STATE_RECEIVE_PONG; err = sendSynchronousCommand(SYNC_CMD_WRITE,conn,"PING",NULL); if (err) goto write_error; return; } ... // [2] if (server.repl_state != REPL_STATE_RECEIVE_PSYNC) { goto error; }

// more}

【1】根据server.repl_state状态,执行对应操作。

从节点发送给主节点的信息,主节点会记录在从节点客户端,并在INFO命令中输出这些信息。另外,Sentinel模块需要从主节点INFO命令响应中获取这些从节点信息。

【2】执行到这里,主从握手阶段已经完成。server.repl_state必须处于REPL_STATE_ RECEIVE_PSYNC状态,否则报错。

下面使用Linux tcpdump工具抓取主从连接报文,分析主从节点握手阶段的通信内容(主节点端口为6000):

tcpdump tcp -i lo -nn port 6000 -T RESP

tcpdump支持RESP协议,最后一个选项-T RESP要求tcpdump以RESP协议格式解析报文。

其中6000端口为主节点端口,60374端口为从节点通信端口。从tcpdump的输出可以清晰地看到主从节点在握手阶段的通信内容。

提示:tcpdump解析后的RESP内容并不会展示数据类型的标志符,如主节点对从节点PING命令的响应实际上是“-NOAUTH Authentication required.”,请读者阅读源码时注意。

以主节点视角分析握手阶段,主节点不断处理来自从节点的命令(包括PING、AUTH、REPLCONF),感兴趣的读者可自行阅读代码。

好了,Redis主从握手流程到此就分析完毕了,这也算是看过源码了,哈哈。

今天的这个内容摘自《Redis核心原理与实践》,想了解更多关于Redis的内容,欢迎阅读此书呀~


杭州某小厂:Redis主从握手流程


书深入地分析了Redis核心功能的内部机制与实现方式,大部分内容源自对Redis源码的分析,并从中总结出实现原理。通过阅读本书,读者可以快速、轻松地了解Redis的内部运行机制。

京东满100-50,可以扫码购买。

抽奖赠书

好了,福利来啦,《Redis核心原理与实践》!

杭州某小厂:Redis主从握手流程

快快拉上你的小伙伴参与进来吧~~

 
   
   
 


如果喜欢本文
欢迎 在看留言分享至朋友圈 三连
往期推荐:


我是yes,从一点点到亿点点,我们下篇见!