Redis 源码剖析与实战
蒋德钧
中科院计算所副研究员
17747 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 47 讲
Redis 源码剖析与实战
15
15
1.0x
00:00/00:00
登录|注册

21 | 主从复制:基于状态机的设计与实现

你好,我是蒋德钧。这节课,我想跟你聊聊 Redis 是如何基于状态机的设计思路,来实现主从复制的。
主从复制技术我们应该都比较熟悉,因为在使用 Redis 或 MySQL 数据库时,我们经常会使用主从复制来实现主从节点间的数据同步,以此提升服务的高可用性。
从原理上来说,Redis 的主从复制主要包括了全量复制、增量复制和长连接同步三种情况。全量复制传输 RDB 文件,增量复制传输主从断连期间的命令,而长连接同步则是把主节点正常收到的请求传输给从节点。
这三种情况看似简单,但是在实现的时候,我们通常都需要考虑主从连接建立、主从握手和验证、复制情况判断和数据传输等多种不同状态下的逻辑处理。
那么,如何才能高效地实现主从复制呢?
实际上,Redis 是采用了基于状态机的设计思想,来清晰地实现不同状态及状态间的跳转。而在我们实现网络功能的时候,这种设计和实现方法其实非常重要,它可以避免我们在处理不同状态时的逻辑冲突或遗漏。所以今天这节课,我就来给你介绍下如何基于状态机实现主从复制。
不过这里我也要说明一点,因为主从复制的状态比较多,如果一下子就学习每个状态细节,我们其实会很容易混淆不同状态的区别和转换关系。所以在今天的课程中,我会先给你介绍下复制整体过程的四个阶段,然后,我们再来逐一学习每个阶段中的状态与变化。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Redis主从复制是一种常见的数据同步技术,通过状态机的设计思路来实现。文章首先介绍了主从复制的四大阶段:初始化、建立连接、主从握手、复制类型判断与执行。在初始化阶段,从库实例会记录主库的IP和端口号,并设置状态机为REPL_STATE_CONNECT。接着,文章详细讲解了基于状态机的主从复制实现,强调了这种设计思路的重要性,以及状态机在不同阶段的变迁和代码实现。通过对redisServer结构体中的相关变量进行分析,读者可以了解状态机在主从复制中的具体应用。整体而言,本文通过清晰的阐述和代码示例,帮助读者深入理解了Redis主从复制的设计与实现。 在建立连接阶段,从库实例会通过周期性任务执行来和主库建立网络连接,并详细介绍了连接的建立过程和状态机的变迁。接着,文章解释了主从握手阶段的状态变迁逻辑,包括验证、IP和端口号传递等过程。最后,文章描述了复制类型判断与执行阶段,包括PSYNC命令的发送和数据同步的实际执行过程。 通过对状态机在不同阶段的应用和状态变迁的详细讲解,读者可以深入了解Redis主从复制的设计思想和实现方法。文章强调了状态机驱动的设计方法的通用性,并提供了良好的参考示例。总之,本文为读者提供了全面的主从复制实现细节,帮助他们更好地理解和应用状态机驱动的方法。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Redis 源码剖析与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(4)

  • 最新
  • 精选
  • Kaito
    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、如果是增量同步,从库只需接收主库传来的增量数据即可 课后题:当一个实例是主库时,为什么不需要使用状态机来实现主库在主从复制时的流程流转? 因为复制数据的发起方是从库,从库要求复制数据会经历多个阶段(发起连接、握手认证、请求数据),而主库只需要「被动」接收从库的请求,根据需要「响应数据」即可完成整个流程,所以主库不需要状态机流转。
    2021-09-21
    29
  • 曾轼麟
    首先回答老师的问题:主库为什么不需要状态机? 在整个复制同步的过程中,我们理解整个流程是这样的:从库初始化->从库发起建立连接->主从握手->复制类型判断与执行,整个流程中会有以下几个特点和场景: 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;
    2021-09-22
    10
  • 夏天
    补充几点: 1.增量同步,主库在每次执行完命令后,会将命令写到 buf 中。由 replicationCron 中的 replicationFeedSlaves,每 100ms 发送一次。
    2022-02-19
    2
    1
  • lhgdy
    为什么需要上报 ip 和端口? 按说服务端是可以通过 fd 获取对端的 ip 和端口的?
    2021-09-22
    2
收起评论
显示
设置
留言
4
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部