Redis主从复制
为了提高性能和系统可用,Redis都会做主从复制,一来可以分担主库压力,二来在主库挂掉的时候从库依旧可以提供服务。Redis的主从复制是异步复制,返回结果给客户端和同步命令到从库是两回事,互不相干,主库也不关心从库的执行结果,对于同步命令执行的结果,从库会直接丢弃并不返回给主库。Redis的主从复制简单高效,但也不太算可靠。
Redis的主从复制是异步复制;全量同步(或增量同步)+命令传播
Slave Server
流程
Slave Server启动初始化配置,根据slaveof配置设置Slave Server的主库host(masterhost)和Slave Server的同步状态(repl_state),和所有Server一样监听客户端链接,开启后台任务。
后台定时任务包含,触发AOF重写、RDB快照、redis监控、状态收集、主从同步相关定时任务等
主从同步后台定时任务包含,从库连接主库、从库重连主库、从库给主库发送同步进度、主库向从库发送心跳包、主库删除超时从库、主库清除同步缓冲区、主库刷新从库状态等
从库连接主库
从库链接主库后,开启同步前的准备和交互,同时从库伴随着和主库交互变换自身状态。下面源代码看一下整个流程(代码有删减)
以上代码有点长,总结一下步骤:1)slave发送自身信息到master;2)尝试增量同步,成功则等待master回送同步数据;3)不支持增量同步或者增量同步失败,则进行全量同步,并等待master回送同步数据。
全量同步(主库基于Disk-backed模式)
1、从库发送sync命令给主库(2.8以上Redis直接使用psync命令)发起全量同步,并等待数据返回
2、主库接收到命令(或者尝试增量同步失败后),把内存数据保存到rdb文件并把文件内容发送给从库
3、从库接收同步数据并保存到本地rdb文件,最后把rdb文件内容写入到内存数据库,至此全量同步完成
以上是简述一下同步流程,但其中并不止那么简单,例如:同时多个slave发起全量同步请求,主库也只会进行一次bgsave,保存内存快照到rdb文件。
rdb是redis持久化的一种,是当前redis内存数据的快照。所以全量同步,主库发送给从库的是,是内存中每一个key和它对应的value(key => value)。
以下源码分析第三步操作(代码有删减)
增量同步
全量同步可以看出存在它的缺点,那就是效率,主从都要进行文件读写,而且还要传输全部数据,其中部分已经在从库中,这部分数据就会显得有点多余了。增量同步,同步的内容也和全量同步有所区别。
全量同步,同步的数据是每一个key和它对应的value(key => value);而增量同步,同步的数据是命令,是从库未执行的命令,例如:set key1 value1 。
执行增量同步的条件:
1)从库已经和该主库进行过一次全量同步
2)主从网络超时(repl_timeout默认是60秒)重连后
3)从库同步进度(reploff)存在并且没有落后于该主库同步缓冲区
|
|
命令传播
完成全量同步(增量同步)后,主库接受客户端命令,修改了数据,为了保持主从数据一致,这些命令也需要在从库上执行一遍,哪怎么操作呢?当然不是客户端逐一修改所有从库了,而是由主库执行命令成功后,异步地把命令发送给所有从库。
这部分主要工作在于主库,就不说了,下一篇主库角度再说。从库接收主库的命令传播,其实和其他客户端的命令一样的行为,只是从库会判断是否来自主库发送的命令,更新自己同步进度,主库不关心从库执行命令的结果,所以从库也不会发送执行结果给主库,省略了一次网络IO。
心跳检测
开篇我们说过,Redis会有个定时任务在后台执行,从库会每秒向主库发送ack+同步进度;主库也会定时发送PING命令检测它所有从库的存活。
风险
Redis的主从复制是很高效,也没有太多花哨的东西,基于异步同步,客户端不需要等待同步结果,但是也是这样的高效同步带来一些风险。
1)主库发送仅且发送一次命令给从库,如果超时,命令丢失,从库没有接收到,会造成不一致;
2)从库内存满了,主库也没法知道,而且从库收到命令传播依旧会更新自己同步进度;
3)异步复制带来的同步间隙,造成短时间内不一致,这点要根据具体业务处理;
4)客户端修改从库数据,也会导致主从不一致,可以把从库设置成只读;
废话
从库未全量同步过
Slave:Master,我要增量同步(psync ? -1)
Master:EXO ME?你没全量同步过,不行,你必须全量同步
Slave:好的,师父。全量同步
Master:同意。等着吧。
(Slave等啊等)
Master:接着,rdb
Slave:收!
(Slave全量同步完后,上线工作了,命令传播)
Master:这是我刚执行完的命令,你执行一下
Slave:好的(执行完了也不告诉你)
Master:这是我刚执行完的命令,你执行一下
Slave:好的(执行完了也不告诉你)
……
主从网络断开重连后
Slave:师父,刚掉线了,我是不是错过了什么,我要增量同步一下(psync Master_id repl_off)
Master:刚哪浪去了?问你又不答我。行了,增量同步一下吧,等着
Slave:好的,师父。
(Slave等啊等)
Master:接着,这些都是你刚没有执行的命令
Slave:收!
(Slave增量同步完后,上线工作了,命令传播)
Master:这是我刚执行完的命令,你执行一下
Slave:好的(执行完了也不告诉你)
Master:这是我刚执行完的命令,你执行一下
Slave:好的(执行完了也不告诉你)
……
主从网络断开重连后,slave落后太多
Slave:师父,刚掉线了,我是不是错过了什么,我要增量同步一下(psync Master_id repl_off)
Master:刚哪浪去了?问你又不答我。不行不行,你落后了(repl_off进度太旧了),你先全量一下我们在聊吧
Slave:好的,师父。全量同步
Master:同意。等着吧。
(Slave等啊等)
Master:接着,rdb
Slave:收!
(Slave全量同步完后,上线工作了,命令传播)
Master:这是我刚执行完的命令,你执行一下
Slave:好的(执行完了也不告诉你)
Master:这是我刚执行完的命令,你执行一下
Slave:好的(执行完了也不告诉你)
……