Redis源码剖析与实战
蒋德钧
中科院计算所副研究员
新⼈⾸单
¥
59
2886 人已学习
课程目录
已更新 27 讲 / 共 33 讲
0/4
登录后,你可以任选
4
讲全文学习。
课前导读 (2讲)
开篇词 | 阅读Redis源码能给你带来什么?
免费
01 | 带你快速攻略Redis源码的整体架构
数据结构模块 (6讲)
02 | 键值对中字符串的实现,用char*还是结构体?
03 | 如何实现一个性能优异的Hash表?
04 | 内存友好的数据结构该如何细化设计?
05 | 有序集合为何能同时支持点查询和范围查询?
06 | 从ziplist到quicklist,再到listpack的启发
07 | 为什么Stream使用了Radix Tree?
事件驱动框架和执行模型模块 (7讲)
08 | Redis server启动后会做哪些操作?
09 | Redis事件驱动框架(上):何时使用select、poll、epoll?
10 | Redis事件驱动框架(中):Redis实现了Reactor模型吗?
11 | Redis事件驱动框架(下):Redis有哪些事件?
12 | Redis真的是单线程吗?
13 | Redis 6.0多IO线程的效率提高了吗?
14 | 从代码实现看分布式锁的原子性保证
缓存模块 (3讲)
15 | 为什么LRU算法原理和代码实现不一样?
16 | LFU算法和其他算法相比有优势吗?
17 | Lazy Free会影响缓存替换吗?
期中测试 (2讲)
期中测试 | 这些Redis源码知识,你都掌握了吗?
期中测试题答案 | 这些问题你都答对了吗?
不定期加餐 (2讲)
加餐1 | Redis性能测试工具的使用
加餐2 | 用户Kaito:我是怎么读Redis源码的?
可靠性保证模块 (5讲)
18 | 如何生成和解读RDB文件?
19 | AOF重写(上):触发时机与重写的影响
20 | AOF重写(下):重写时的新写操作记录在哪里?
21 | 主从复制:基于状态机的设计与实现
22 | 哨兵也和Redis实例一样初始化吗?
Redis源码剖析与实战
15
15
1.0x
00:00/00:00
0.75x
1.0x
1.25x
1.5x
2.0x
登录
|
注册
21 | 主从复制:基于状态机的设计与实现
蒋德钧
2021-09-21
你好,我是蒋德钧。这节课,我想跟你聊聊 Redis 是如何基于状态机的设计思路,来实现主从复制的。
主从复制技术我们应该都比较熟悉,因为在使用 Redis 或 MySQL 数据库时,我们经常会使用主从复制来实现主从节点间的数据同步,以此提升服务的高可用性。
从原理上来说,Redis 的主从复制主要包括了
全量复制、增量复制和长连接同步
三种情况。全量复制传输 RDB 文件,增量复制传输主从断连期间的命令,而长连接同步则是把主节点正常收到的请求传输给从节点。
这三种情况看似简单,但是在实现的时候,我们通常都需要考虑主从连接建立、主从握手和验证、复制情况判断和数据传输等多种不同状态下的逻辑处理。
那么,
如何才能高效地实现主从复制呢?
实际上,Redis 是采用了
基于状态机
的设计思想,来清晰地实现不同状态及状态间的跳转。而在我们实现网络功能的时候,这种设计和实现方法其实非常重要,它可以避免我们在处理不同状态时的逻辑冲突或遗漏。所以今天这节课,我就来给你介绍下如何基于状态机实现主从复制。
不过这里我也要说明一点,因为主从复制的状态比较多,如果一下子就学习每个状态细节,我们其实会很容易混淆不同状态的区别和转换关系。所以在今天的课程中,我会先给你介绍下复制整体过程的四个阶段,然后,我们再来逐一学习每个阶段中的状态与变化。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/1000字
划线
笔记
复制
©
版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Redis源码剖析与实战》,如需阅读全部文章,
请订阅文章所属专栏
,
新⼈⾸单
¥
59
立即订阅
登录
后留言
精选留言(3)
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
2
lhgdy
为什么需要上报 ip 和端口? 按说服务端是可以通过 fd 获取对端的 ip 和端口的?
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;
2021-09-22
收起评论
3
下载
客户端
返回
顶部