Redis具有高可靠性,主要体现在两方面,一是数据尽量少丢失,二是服务尽量少中断。AOF和RDB保证了第一点,而第二点的实现,Redis采用增加副本冗余量的方式。
Redis提供主从库模式来保证数据副本一致,主从库采用读写分离。读操作可以通过主库或从库,而写操作首先到主库执行,再由主库将写操作同步给从库。
为什么要采用读写分离的方式呢?
如果客户端对同一个数据进行多次修改,每次写操作都发送到不同的实例上,会导致实例副本数据不一致。这种情况下如果要做到数据一致,就会涉及到加锁、实例之间协商是否完成修改等一系列操作,带来巨额开销。而读写分离把所有写操作的请求都发送到主库上,然后同步给从库,多个实例之间就不用协调了。
主从库同步是如何完成的?
启动多个Redis实例的时候,通过replicaof命令形成主从库关系。(Redis 5.0以前使用slaveof)
第一阶段,从库和主库建立连接,告诉主库即将进行同步,主库确认恢复后,主从库间就可以开始同步了,psync命令包含runID和offset两个参数,runID是主库实例的随机ID,由于第一次连接时不知道主库runID,所以是“?”,offset是复制进度,-1表示第一次复制。主库收到psync命令后,使用FULLRESYNC命带上runID和offset返回给从库,FULLRESYNC表示第一次复制采用全量复制。
第二阶段,主库将所有数据同步给从库,从库收到数据后,在本地完成清空当前数据并重新进行数据加载。
第三阶段,主库把第二阶段实行过程中收到的写命令记录在replication buffer中,当主库完成RDB文件发送后,再把replication buffer中的写操作发送给从库。这样就实现主从同步了。
如果有多个从库,而且都要和主库进行全量复制的话,就会导致主库相应应用程序的请求变慢,可以通过“主-从-从”模式,将主库生成RDB和传输RDB的压力,以级联的方式分散到从库上。
一旦从库完成了全量复制,他们会一直维护一个网络连接,主库会通过这个连接将后续收到的命令操作同步给从库。这个过程称为基于长连接的命令传播。
主从库间网络断了怎么办?
Redis 2.8开始,网络中断后,主从库采用增量复制的方式继续同步,把主从库网络断开期间主库收到的命令,同步给从库。
当主从断连后,主库会把断连期间收到的写操作命令,写入repl_backlog_buffer这个缓冲区。repl_backlog_buffer是一个环形缓冲区,主库会记录自己写到的位置,从库会记录自己已经读到的位置。
由于repl_backlog_bugger是环形缓冲区,所以缓冲区写满后,主库还会继续写入,就会覆盖之前的写入操作。如果从库读取的速度比较慢,可能会导致从库还未读取的操作被主库写的操作覆盖了,这会导致主从库间的数据不一致。
为了避免这种情况,可以调整repl_backlog_size这个参数调整缓冲空间。缓冲空间大小 = 主库写入命令速度 * 操作大小 - 主从库间网络传输命令速度 * 操作大小。在实际应用中,还需考虑可能存在的突发请求压力,通常需要把缓冲空间扩大一倍,即repl_backlog_sieze = 缓冲空间大小 * 2;如果并发请求量非常大,两倍缓冲空间都存不下新操作的话,可以考虑继续增大缓冲空间,并使用切片集群来分担单个主库的请求压力。
全量同步为什么使用RDB而不用AOF?
- RDB是压缩的二进制数据,AOF文件记录的是操作命令,使用RDB进行主从同步的成本最低;
- 如果使用AOF做主从同步,就必须打开AOF功能,选择文件刷盘策略,选择不当会影响性能,而RDB只有在需要定时备份和主从全量同步数据时才处罚生成快照;
repl_backlog_buffer和replication buffer的区别?
- repl_backlog_buffer:它是为了从库断连后找到主从差异而设计的环形缓冲区。如果从库断连时间太久,repl_backlog_buffer的环形缓冲区被主库的写命令覆盖了,那么从库也只能进行一次全量同步,所以将repl_backlog_buffer配置尽量大一些,可以降低主从断开后全量同步的概率。而在repl_backlog_buffer中找到差异数据后,就用到了replication buffer;
- replication buffer:Redis和客户端通信或者和从库通信,都需要分配一个内存buffer进行数据交互,实际上客户端和从库都可以看作是一个client,每一个client连接Redis后,Redis都会分配一个client buffer,所有数据交互都是通过这个buffer进行的。主从增量同步时,从库作为一个client,也会分配一个buffer,只不过这个buffer专门用来传播用户的写命令到从库,保证主从一致,这个buffer通常把它叫做replication buffer;
- 如果主从在传播命令时,从库处理得非常慢,那么主库的replication buffer就会持续增长,消耗大量内存资源,甚至OOM。所以Redis提供了client-output-buffer-limit参数限制这个buffer的大小,如果超过限制,主库会强制断开这个client连接,复制中断,中断后如果从库再次发起复制请求,可能会造成恶行循环,引发复制风暴。