主从数据库配置用来降低单个redis的压力(主要是master的读压力)。通常的方案是master用做数据写入,slave用做数据读取。
下面看看主从复制的功能是如何实现的,首先需要在slave服务上配置master的ip端口(slaveof host port),可选项数据库密码(masterauth)。
也可以起一个客户端连接slave发送slaveof host port 命令,相应的取消命令是slaveof no one。
一个slave接收到slaveof命令以后经历一下过程。
1.与原有的master 断开连接,取消同步操作。
2.设置当前的服务为slave,记录master的地址信息。将server.repl_state置为REDIS_REPL_CONNECT;
3.在下个时间事件中调用事件周期函数replicationCron,检测repl_state的值,执行连接master操作。连接失败则后续重复步骤3。
4.发一个ping命令证明master是能正常提供服务的,认证密码,发送replconf命令,告知master本slave监听端口。
5.发送“psync ? -1”命令获取全量获取master上的rdb数据,如果不支持psync,那么可能是早期版本,发送sync命令。
6.将拿到的rdb文件存储下来,删去slave上原有的数据,加载rdb文件到内存中,这一步和redis启动的过程一致。
至此,复制流程完成,如果master上有新的数据更新,master会主动调用replicationFeedSlaves函数将增量数据发送给他的所有slave。这么看master相当于slave的客户机了。
我们默认不能往slave上直接执行写操作,slave会返回一个read-only的错误,当然我们可以使用"config set slave-read-only yes"命令启用slave的写功能, 但是这些数据不会被同步到master及其其他从属的slaves上。
以上我们是以slave的角度观察这个操作,下面我们将以master的角度再次走查这个流程,主要是psync命令。
1.根据slave发来的runid判断是否需要fullsync。其中runid是master启动时随机生成的字符串。
2.将该slave加入server.slaves列表,并将它的replstate置为REDIS_REPL_WAIT_BGSAVE_END,开启一个bgsave来保存rdb文件,(如果已经在bgsave了就跳过)
3.bgsave完成以后,循环便利所有状态为REDIS_REPL_WAIT_BGSAVE_END的节点,将rdb文件发送给他们。
4.周期性发送心跳,断开超时的slave。
master上bgsave期间的客户端发来的数据如何同步到slave?。sync命令执行之前,需要确保slave的消息缓存是空的,在全量同步到数据之前,发送到该slave上的数据均会被缓存,不会触发aeCreateFileEvent,等同步完成了以后,会触发一个可写事件将缓存发送出去。
至此,slave从master拉取数据完毕。后续如果客户端有写操作了,master执行完客户端的写操作请求以后,会将命令传播给他的从机,从而保证数据一致性。