redis6.0源码阅读 主从模式之数据同步
给新观众老爷的开场
大家好,我是弟弟!
最近读了一遍 黄健宏大佬的 <<Redis 设计与实现>>,对Redis 3.0版本有了一些认识
该书作者有一版添加了注释的 redis 3.0源码
👉官方redis的github传送门。
👉黄健宏大佬添加了注释的 redis 3.0源码传送门
👉antirez的博客
网上说Redis代码写得很好,为了加深印象和学习redis大佬的代码写作艺术,了解工作中使用的redis 命令背后的源码逻辑,便有了写博客记录学习redis源码过程的想法。
今天斗胆打开了redis 6.0的代码开始阅读理解。
redis6.0相对于redis3.0增加了不少的功能。
本篇博客的描述方式以主从模式的数据同步为主线
,其中涉及其他的东西为支线
。
主从模式-数据同步的原因
1. 数据热备份
从节点热备份主节点的数据,在哨兵 或 集群模式下。
主节点挂掉的时候,可以通过raft算法选择一个数据最新的从节点作为新的节点。
如果通过重启主节点,并利用主节点的rdb/aof文件来恢复数据,一个是慢,一个是可能会丢失较多的数据,在恢复数据期间,无法对外提供服务。
而实时备份的从节点,拥有与最新数据最接近的现成数据,将其提升为主机节点后可立即对外提供服务。
2. 读写分离
当单个主节点承受不了读/写压力时,
一种方式是,可以将读压力负载均衡分发到从节点,从而减轻主节点的读压力,但读写分离会存在一定的延迟。
热key问题,读写分离是一种解决方案。
另一种redis 6.0官方的客户端缓存方案,这个方案跟平时从redis中取出值之后再做本地内存缓存差不多是一个意思,不过官方的方案似乎有过期key通知,这个以后有时间再研究研究。
3. 从节点需要与主节点同步数据
新增一个从节点,或主从切换 从节点需与主节点数据同步
主从模式-数据同步的场景
从节点数据与主节点数据不一致
数据同步的大背景就是从节点数据与主节点数据不一致。😂 苍白无力的废话。
而该大背景可以分为一些具体的场景。
1. 给主节点新加入从节点 - 全量同步
新加入的从节点啥也没有
2. 从节点与主节点数据同步中断
中断的原因有很多,比如从节点挂了重启,主节点挂了重启,或者选举了另外一个从节点做主节点,网络不通等等…
根据从节点与主节点数据不一致的程度大小可以分为
- 不一致程度较小的,可以断点续传的部分重传
- 不一致程度较大的,不可以断点续传的全量同步
主从模式-数据同步的思路
全量同步
主节点向从节点写入完整的rdb文件流,随后写入这期间增量的命令
部分重传
利用复制积压缓冲区的增量同步,
也就是主节点会缓存最近一段时间执行的需要同步的增量命令,
如果从节点缺失的部分数据刚好在复制积压缓冲区中能找到,
那就可以仅同步缺失的部分数据。而不用全量同步
主从模式-数据同步的实现
向一个非redis_cluster模式下的redis实例B 发送 slave of master_ip master_port,
即可触发 将该redis实例 作为 目标redis实例的从节点的主从同步过程。
- 从节点会标记当前与主节点同步进行到哪一步了,也就是从节点的复制状态。
- 对于主节点来说,从节点会作为一个 client_slave加入到主节点的客户端链表上。
并且主节点里的从节点的客户端结构里也会有一个字段来标记 同步到哪一步了,也就是同步状态。
下面将主要以上述两种状态的变化,以及每个状态下需要做的事情为线索描述详细的同步过程
先来看一眼从节点内部的复制状态,后面会详细描述每个状态 👇
./server.h 头文件里的定义
/* Slave replication state. Used in server.repl_state for slaves to remember
* what to do next. */
#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 */
#define REPL_STATE_SEND_PORT 6 /* Send REPLCONF listening-port */
#define