环境说明:redis源码版本 5.0.3;我在阅读源码过程做了注释,git地址:https://gitee.com/xiaoangg/redis_annotation
如有错误欢迎指正
参考书籍:《redis的设计与实现》
文章推荐:
redis源码阅读-一--sds简单动态字符串
redis源码阅读--二-链表
redis源码阅读--三-redis散列表的实现
redis源码浅析--四-redis跳跃表的实现
redis源码浅析--五-整数集合的实现
redis源码浅析--六-压缩列表
redis源码浅析--七-redisObject对象(下)(内存回收、共享)
redis源码浅析--八-数据库的实现
redis源码浅析--九-RDB持久化
redis源码浅析--十-AOF(append only file)持久化
redis源码浅析--十一.事件(上)文件事件
redis源码浅析--十一.事件(下)时间事件
redis源码浅析--十二.单机数据库的实现-客户端
redis源码浅析--十三.单机数据库的实现-服务端 - 时间事件
redis源码浅析--十三.单机数据库的实现-服务端 - redis服务器的初始化
redis源码浅析--十四.多机数据库的实现(一)--新老版本复制功能的区别与实现原理
redis源码浅析--十四.多机数据库的实现(二)--复制的实现SLAVEOF、PSYNY
redis源码浅析--十五.哨兵sentinel的设计与实现
redis源码浅析--十六.cluster集群的设计与实现
redis源码浅析--十七.发布与订阅的实现
redis源码浅析--十八.事务的实现
redis源码浅析--十九.排序的实现
redis源码浅析--二十.BIT MAP的实现
redis源码浅析--二十一.慢查询日志的实现
redis源码浅析--二十二.监视器的实现
复制功能相关概念&搭建方法可以参考:https://blog.csdn.net/qq_16399991/article/details/99881319
目录
一 旧版复制功能
名词解释
redis复制功能分为 同步(sync)和命令传播(command propagate)两个操作;
- 同步(sync):将slave数据库状态更新至master所处的数据状态;(全量操作)
- 命令传播(command propagate):当master执行写命令,导致主从数据不一致时。将写命令传播到slave,从而达到一致状态;
1.1.同步
当slave接受到salveof命令,从服务首先同步操作,即像master发送SYNC命令,执行流程如下:
- slave向master发送SYNC命令;
- master收到SYNC命令后执行BGSAVE命令,在后台生成RDB文件,并用一个缓冲区记录从现在开始的所有写命令;
- master的BGSAVE执行完毕后,master将生成的RDB文件发送给slave;
- slave收到RDB文件后,将数据重做到slave中;
- master将步骤2中缓冲区记录的命令发送给salve。从而达到master和slave数据一致;
1.2.命令传播
同步完成后,当maser再次执行写命令,还会导致master和slave数据的不一致;
为了让master和slave数据保持一致,master在执行写命令时,master会将写命令发送给slave,slave执行写命令后,使master和slave数据保持一致;
1.3.旧版复制功能的缺陷
先看一个复制功能完整例子:
时间 | master | slave | |
---|---|---|---|
T0 | 服务启动 | 同步 | |
T1 | 执行命令 SET k2 v2; SET k3 v3: | ||
T3 | 服务启动; | ||
T4 | 向master发送SYNC命令; | ||
T5 | 接受到了SYNC命令;执行BGSAVE; | ||
T6 | BGSAVE还没执行完毕; 执行命令 SET k4 v4; SET k5 v5; 这时k4和k5记录到了缓冲区; | ||
T7 | BGSAVE命令执行完毕,这时生成的RDB文件包含k1,k2和k3; 将RDB文件发送给slave | ||
T8 | slave接受到RDB,重做;slave获得k1 k2 k3 | ||
... | 将 缓冲区中 set k4 v4 ;set k5 v5 发送给slave; | ||
.... | 接受到 set k4 v4 ;set k5 v5; | ||
(这时同步完成 数据一致); | |||
T99 | set k6 v6; ...... set k100000 v100000; 发送到slave | 接受master set k6 v6; ..... set k10000 v10000 | 命令传播 |
T100 | 假设salve因为网络原因与master断开连接; | 断线重新复制 | |
T101 | set kk1 vv1; set kk3 vv3; | 掉线中... 掉线中... | |
T102 | 上线; 向master发送SYNC, | ||
T103 | BGSAVE ..... 发送RDB | ||
T104 | 重做RDB |
在时间T100,slave因为网络原因与master断开了连接;
时间T102,slave重新连接上了master,这时master和slave数据已经不一致了。(但是只有 set kk1 vv1, set kk2 vv2 set kk3 vv3 不一致;)
slave发送SYNC命令,让master 和slave再次回到一致状态;
但是仔细研究这个断线 重新复制的过程,发现BGSAVE和传送RDB的过程并不是必做不可的。
因为这时只有KK1 KK2 KK3是不一致的。而RDB中k....k10000 都是不必要的;
SYNC命令事一个非常耗费资源的操作
- BGSAVE生成RDB文件会耗费大量CPU、内存、磁盘I/O资源;
- RDB文件传输给slave会消耗大量的网络资源(带宽和流量);
- slave接受RDB文件,载入期间 从服务器会堵塞,无法接受命令请求;
二.新版复制功能
为了解决旧版处理断线重复复制的低效问题。redis从2.8使用PSYNC来替代SYNC命令;
名词解释
- 完整同步(full resynchronization):完整重同步执行和SYNC执行步骤基本一致
- 部分重同步(partial resynchronization):部分重复制用于处理断线后重复复制状况;当slave断线后重新连接主服务器时,如果条件允许(什么条件:下边的2.1.2 复制积压缓冲区有介绍),主服务器可以将断线期间的命令发送给slave。 (上例子中将会只发送kk1 kk2 kk3 给slave,节省很多资源)
举个例子:
时间 | master | slave | |
---|---|---|---|
.... | .... | ..... | .... |
T100 | 假设salve因为网络原因与master断开连接; | 断线重新复制 | |
T101 | set kk1 vv1; set kk3 vv3; | 掉线中... 掉线中... | |
T102 | 上线; 向master发送PSYNC | ||
T103 | 像slave 发送+CONTINUE回复,表示执行部分重同步 | ||
T104 | 接受+CONTINUE回复,执行部分重同步 | ||
向slave发送set kk1 vv1, set kk2 vv2, set kk2 vv3 | |||
接受 set kk1 vv1。 |
2.1.部分重同步实现
重要概念
- 复制偏移量
- master 复制积压缓冲区
- 服务器运行ID
2.1.1复制偏移量
master和slave都维护了一个复制偏移量;
master每次向slave发送N个字节数据时,master的偏移量就会加上N;
同理 slave每次接收到master的N个字节数据时,slave的偏移量也会加上N;
如果master和slave的偏移量是相同的,那么主从数据处于一致的状态;
举个例子:
1.master和slave的偏移量都是10086(这时数据是一致的):
2.这时候master向slave发送了33个字节,并且三个slave都正常接受到了;偏移量加33变成了10119;
如果master和slave的偏移量是不同的,那么主从数据不一致;
举个例子:
slave A断线,导致slave A没有接受到mater发送的33个字节数据;
这时 maser的偏移量变成了10119 ,slave A的因为没有接受到数据 偏移量并没有加33。master和slave数据不一致;
2.1.2 复制积压缓冲区
复制积压缓冲区是主服务器维护的一个固定长度的的队列(先进先出 默认1MB);
当master向slave传播命令时,会将命令写入到复制积压缓冲区;
因此复制积压缓冲区记录了最近向slave传播的命令;并且为每个字节记录了相应的复制偏移量(这很重要);
当slave断线后重新连接master时,向master发送PSYNC命令 会将自己的复制偏移量发送给master。
master会根据这个偏移量决定对slave执行部分同步还是完全同步;
- slave的偏移量在复制积压缓冲区,执行部分同步 ;
- slave的偏移量不在复制积压缓冲区,则执行完成同步;
2.1.3 服务器运行ID
每个服务器都有自己的运行id,不论主从;
slave对master初次复制时,会保存master的运行id;
当slave重新连接到master时,slave向master发送之前保存的mater run id;
如果slave保存的master run id和重新连接的master run id不一致,(换了master),则执行完全同步;
相反,如果一致则尝试执行部分同步;
。。。。。。。
了解基础原理后,后面详细介绍代码的实现过程。。。