Kaito
2021-09-21
1、Redis 主从复制分为 4 个阶段: - 初始化 - 建立连接 - 主从握手 - 数据传输(全量/增量复制) 2、主从复制流程由于是是「从库」发起的,所以重点要看从库的执行流程 3、从库发起复制的方式有 3 个: - 执行 slaveof / replicaof 命令 - 配置文件配置了主库的 ip port - 启动实例时指定了主库的 ip port 4、建议从 slaveof / replicaof 命令跟源码进去,来看整个主从复制的流程(入口在 replication.c 的 replicaofCommand 函数) 5、从库执行这个命令后,会先在 server 结构体上,记录主库的 ip port,然后把 server.repl_state 从 REPL_STATE_NONE 改为 REPL_STATE_CONNECT,「复制状态机」启动 6、随后从库会在定时任务(server.c 的 serverCron 函数)中会检测 server.repl_state 的状态,然后向主库发起复制请求(replication.c 的 replicationCron 函数),进入复制流程(replication.c 的 connectWithMaster 函数) 7、从库会与主库建立连接(REPL_STATE_CONNECTING),注册读事件(syncWithMaster 函数),之后主从进入握手认证阶段,从库会告知主库自己的 ip port 等信息,在这期间会流转多个状态(server.h 中定义的复制状态): #define REPL_STATE_RECEIVE_PONG 3 /* Wait for PING reply */ #define REPL_STATE_SEND_AUTH 4 /* Send AUTH to master */ #define REPL_STATE_RECEIVE_AUTH 5 /* Wait for AUTH reply */ #define REPL_STATE_SEND_PORT 6 /* Send REPLCONF listening-port */ #define REPL_STATE_RECEIVE_PORT 7 /* Wait for REPLCONF reply */ #define REPL_STATE_SEND_IP 8 /* Send REPLCONF ip-address */ #define REPL_STATE_RECEIVE_IP 9 /* Wait for REPLCONF reply */ #define REPL_STATE_SEND_CAPA 10 /* Send REPLCONF capa */ #define REPL_STATE_RECEIVE_CAPA 11 /* Wait for REPLCONF reply */ 8、完成握手后,从库向主库发送 PSYNC 命令和自己的 offset,首先尝试「增量同步」,如果 offset = -1,主库返回 FULLRESYNC 表示「全量同步」数据,否则返回 CONTINUE 增量同步 9、如果是全量同步,主库会先生成 RDB,从库等待,主库完成 RDB 后发给从库,从库接收 RDB,然后清空实例数据,加载 RDB,之后读取主库发来的「增量」数据 10、如果是增量同步,从库只需接收主库传来的增量数据即可 课后题:当一个实例是主库时,为什么不需要使用状态机来实现主库在主从复制时的流程流转? 因为复制数据的发起方是从库,从库要求复制数据会经历多个阶段(发起连接、握手认证、请求数据),而主库只需要「被动」接收从库的请求,根据需要「响应数据」即可完成整个流程,所以主库不需要状态机流转。
展开
26
曾轼麟
2021-09-22
首先回答老师的问题:主库为什么不需要状态机? 在整个复制同步的过程中,我们理解整个流程是这样的:从库初始化->从库发起建立连接->主从握手->复制类型判断与执行,整个流程中会有以下几个特点和场景: 1、整个流程中,发起方本身是从库。 2、主库无需知道每个从库的所属的环节和状态(响应式)。 3、主库随时可能会因为灾备切换(状态丢失)。 那么如果主库维护了状态机,那么会出现一下几个问题需要处理: 1、主库的状态机是否需要灾备转移? 2、主库需要给每个从库的client维护一个状态机进行冗余。 综合来说,主库增加状态机在功能上没办法带来较大的优化,还有可能会带来一些不必要的问题,所以主库没必要维护一套状态机。 总结: 本篇文章老师主要介绍了主从复制中,基于状态机的实现。在大多中间件,如kafka也是有多套状态机实现的,其主要的目的有以下几个点: 1、维护众多状态的先后顺序和扭转变化逻辑 2、确定状态的边界(有多少种状态) 3、维护当前状态。 在5.0以及之前Redis版本中状态是统一维护在server.h文件中的宏定义里面的,如下所示: #define REPL_STATE_NONE 0 /* No active replication */ #define REPL_STATE_CONNECT 1 /* Must connect to master */ #define REPL_STATE_CONNECTING 2 /* Connecting to master */ /* --- Handshake states, must be ordered --- */ #define REPL_STATE_RECEIVE_PONG 3 /* Wait for PING reply */ #define REPL_STATE_SEND_AUTH 4 /* Send AUTH to master */ #define REPL_STATE_RECEIVE_AUTH 5 /* Wait for AUTH reply */ 等等..... 而在之后的版本中,维护了类似repl_state这样一套状态枚举 typedef enum { REPL_STATE_NONE = 0, /* No active replication */ REPL_STATE_CONNECT, /* Must connect to master */ REPL_STATE_CONNECTING, /* Connecting to master */ /* --- Handshake states, must be ordered --- */ REPL_STATE_RECEIVE_PING_REPLY, /* Wait for PING reply */ REPL_STATE_SEND_HANDSHAKE, /* Send handshake sequence to master */ REPL_STATE_RECEIVE_AUTH_REPLY, /* Wait for AUTH reply */ REPL_STATE_RECEIVE_PORT_REPLY, /* Wait for REPLCONF reply */ REPL_STATE_RECEIVE_IP_REPLY, /* Wait for REPLCONF reply */ REPL_STATE_RECEIVE_CAPA_REPLY, /* Wait for REPLCONF reply */ REPL_STATE_SEND_PSYNC, /* Send PSYNC */ REPL_STATE_RECEIVE_PSYNC_REPLY, /* Wait for PSYNC reply */ /* --- End of handshake states --- */ REPL_STATE_TRANSFER, /* Receiving .rdb from master */ REPL_STATE_CONNECTED, /* Connected to master */ } repl_state;
展开
8
夏天
2022-02-19
补充几点: 1.增量同步,主库在每次执行完命令后,会将命令写到 buf 中。由 replicationCron 中的 replicationFeedSlaves,每 100ms 发送一次。
共 2 条评论
1
lhgdy
2021-09-22
为什么需要上报 ip 和端口? 按说服务端是可以通过 fd 获取对端的 ip 和端口的?
共 2 条评论