高可用有两层含义:一是尽可能避免数据丢失,二是尽可能提供服务。AOF和RDB尽可能保证数据持久化不丢失,而主从复制是增加副本,一份数据保存到多个实例。即使一个实例宕机,其他实例仍然可以提供服务。本文主要带大家了解Redis高可用技术方案之一的主从复制架构。核心知识点开场留言问题=机会。遇到问题的时候,心里其实是快乐的。问题越大,机会就越大。一切都是有代价的。有得必有失,有失必有得,所以很多事情都不用操心。我们只要想清楚自己想做什么,愿意为此付出什么样的代价,然后放手去做吧!一、主从复制概述65师兄:有了RDB和AOF,再也不怕宕机丢失数据了,但是Redis实例宕机时如何实现高可用呢?既然一台机器宕机不能提供服务,那么有多少台?能解决吗?Redis提供了主从模式,通过主从复制,将数据冗余复制到其他Redis服务器。前者称为主节点(master),后者称为从节点(slave);数据复制是单向的,只从主节点到从节点。默认情况下,每个Redis服务器都是一个主节点;一个主节点可以有多个从节点(或者没有从节点),但是一个从节点只能有一个主节点。65师兄:如何保证master和slave之间数据的一致性?为了保证副本数据的一致性,主从架构采用了读写分离的方式。读操作:主从库都可以执行;写操作:主库先执行,然后写操作同步到从库;Redis读写分离65哥:为什么要用读写分离的方式?我们可以假设主从库都可以执行写命令。如果多次修改同一个数据,每次修改都发送到不同的主从实例,会导致实例的副本数据不一致。如果Redis为了保证数据的一致性,需要加锁协调多个实例的修改,Redis自然不会这么做!65哥:主从复制还有其他作用吗?故障恢复:当主节点宕机时,其他节点仍然可以提供服务;负载均衡:Master节点提供写服务,Slave节点提供读服务,分担压力;高可用基石:是哨兵和集群实现的基础,是高可用的基石。2.搭建主从复制主从复制的启动完全是在从节点上发起的,我们不需要在主节点上做任何事情。65师兄:如何搭建主从复制架构?可以使用replicaof(Redis5.0之前的slaveof)命令形成主从库关系。从节点开启主从复制的方式有以下三种:在从服务器的配置文件中添加replicaofstartup命令,在从服务器的启动命令后添加--replicaofclient命令。执行命令:replicaof,则Redis实例成为从节点。例如,假设有实例1(172.16.88.1)、实例2(172.16.88.2)和实例3(172.16.88.3),分别在实例2和实例3上执行以下命令,实例2和实例3变成实例1从库中,实例1成为主实例。副本172.16.88.163793。主从复制原理主从库模式一旦采用读写分离,所有的数据写入操作只会在主库上进行,不协调三个实例。主库有最新数据后,会同步到从库,使主库和从库的数据保持一致。65师兄:主从数据库同步是如何完成的?主库数据是一次性传到从库,还是分批同步?正常运行时如何同步?如果主从数据库之间的网络断开了,重新连接之后数据还能保持一致吗?65哥,你怎么这么多问题啊?同步分为三种情况:主从数据库第一次全量拷贝;主从正常运行时的同步;主从数据库断网重连。主从库的第一个完整副本65哥:我晕了,还是先从主从库之间的第一个同步开始吧。主从库的第一次复制过程大致可以分为三个阶段:连接建立阶段(即准备阶段)、主库同步数据到从库阶段、发送新的write同步到从库阶段的命令;有一种整体观的感知,后面会详细介绍。Redis全量同步连接建立该阶段的主要作用是建立主从节点之间的连接,为全量数据同步做准备。从库会与主库建立连接。从库执行replicaof并发送psync命令,告诉主库即将进行同步。主库确认回复后,主从库开始同步。65师兄:从库如何知道主库的信息并建立连接?在从节点的配置文件中的replicaof配置项中配置好主节点的IP和端口后,从节点就知道自己要向上连接主节点了。从节点内部维护了两个字段,masterhost和masterport,用于存储主节点的IP和端口信息。从库执行replicaof,发送psync命令,表示要进行数据同步。主库收到命令后,根据参数开始复制。该命令包含两个参数,主库的runID和复制进度偏移量。runID:每次Redis实例启动都会自动生成一个唯一的ID。第一次主从复制,master库的runID未知,参数设置为“?”。offset:第一个副本设置为-1,表示第一个副本,记录副本进度的offset。主库收到psync命令后,会使用带有两个参数的FULLRESYNC响应命令:主库runID和主库当前复制进度偏移量,返回给从库。当从库中收到响应时,将记录这两个参数。FULLRESYNC响应表示第一次拷贝使用的全量拷贝,即主库会将当前所有数据拷贝到从库。主库同步数据到从库。第二阶段,master执行bgsave命令生成RDB文件,发送给从库。同时,master库为每个slave创建一个replicationbufferbuffer,用于记录从RDB文件生成开始接收到的所有写入。命令。从库中接收到RDB文件后,保存到磁盘,清除当前数据库中的数据,再将RDB文件数据加载到内存中。向从库发送新的写入命令。第三阶段从从节点加载RDB后,主节点将replicationbufferbuffer中的数据发送给从节点,从节点接收并执行。从节点同步到与主节点相同的状态。65兄:主库在同步数据到从库的过程中能否正常接受请求?主库不会被阻塞。Redis作为唯一一个快而不破的人,动不动就可以挡。RDB文件生成后的写操作,并没有记录到刚才的RDB文件中。为了保证主从库数据的一致性,主库会在内存中使用一个replicationbuffer来记录RDB文件生成后的所有写操作。65兄:为什么从从库接收到RDB文件后需要清空当前数据库?因为从库在开始通过replcaof命令与主库同步之前可能会保存其他数据,防止主从数据相互影响。复制缓冲区到底是什么?在master端创建的一个buffer,存放的数据是master后面三个时期的所有数据写操作。1)master期间的写操作执行bgsave生成RDB;2)master发送rdb到slave网络传输期间的写操作;3)slave加载rdb文件时的写操作,将数据恢复到内存中。Redis无论是与客户端通信还是与从库通信,Redis都会分配一块内存缓冲区用于数据交互。客户端是客户端,从库也是客户端。我们每个客户端连接到Redis后,Redis都会分配一个专有的客户端缓冲区,所有的数据交互都是通过这个缓冲区进行的。Master先把数据写入这个buffer,然后通过网络发送出去,这样就完成了数据交互。不管主从是增量同步还是全量同步,master都会为其分配一个buffer,但是这个buffer是专门用来向从库传播写命令,保证主从数据一致的。我们通常称它为复制缓冲区。replicationbuffer太小导致的问题:replicationbuffer是由client-output-buffer-limitslave设置的。当这个值太小时,主从复制连接会断开。1)当主从复制连接断开时,master会释放连接相关的数据。复制缓冲区中的数据也丢失了,主从之间重新开始复制??过程。2)还有一个更严重的问题。主从复制连接断开,导致在主从上重新执行bgsave和rdb重传操作死循环。当主节点数据量较大,或者主从节点之间的网络延迟较大时,缓冲区的大小可能会超过限制,此时主节点会断开与从节点的连接;这种情况可能会造成Fullcopy->replicationbufferoverflowleadstoconnectioninterrupt->reconnection->fullcopy->replicationbufferbufferoverflowleadstoconnectioninterrupt...循环。具体细节:【topredisheadachesfordevops–replicationbuffer】因此,建议将replicationbuffer的硬/软限制设置为512M。configsetclient-output-buffer-limit"slave5368709125368709120"65哥:主从数据库复制为什么不用AOF?与RDB相比,丢失的数据更少。这是一个很好的问题,原因如下:RDB文件是二进制文件,RDB的网络传输和写入磁盘的IO效率高于AOF。从数据库中恢复数据时,RDB的恢复效率也高于AOF。增量复制65哥:主从库之间网络断了怎么办?断开连接后是否需要完整复制?Redis2.8之前,如果主从库在命令传播过程中出现网络中断,那么从库会再次和主库进行全量拷贝,代价非常大。从Redis2.8开始,断网后,主从库会继续使用增量复制进行同步。增量复制:用于网络中断等情况后的复制。只将中断期间主节点执行的写命令发送给从节点,比全复制效率更高。repl_backlog_buffer断连重连增量复制的秘诀就是repl_backlog_buffer缓冲区。每当master将写命令操作记录在repl_backlog_buffer中,由于内存有限,repl_backlog_buffer是一个定长的循环数组。如果数组已满,它会从头开始覆盖之前的内容。master用master_repl_offset记录自己写入的位置offset,slave用slave_repl_offset记录自己读取的offset。master收到写操作,offset递增。从库继续执行同步写指令后,repl_backlog_buffer中的replicatedoffsetslave_repl_offset也在增加。一般情况下,这两个偏移量基本相等。在断网阶段,主库可能会收到新的写操作命令,所以master_repl_offset会大于slave_repl_offset。repl_backlog_buffer当master和slave断开重连时,slave会先向master发送psync命令,同时将自己的runID和slave_repl_offset发送给master。master只需要将master_repl_offset和slave_repl_offset之间的命令同步到从库即可。增量复制执行流程如下图所示:Redis增量复制65师兄:repl_backlog_buffer太小,还没从库读出就被Master新的写操作覆盖了怎么办?我们必须想办法避免这种情况。将执行完整复制。我们可以调整repl_backlog_size参数来控制缓冲区大小。计算公式:repl_backlog_buffer=second*write_size_per_second秒:从服务器断开连接并重新连接到主服务器所需的平均时间;write_size_per_second:master平均每秒产生的命令数据量(写命令与数据大小之和);例如,如果主服务器平均每秒产生1MB的写入数据,从服务器断开连接后平均需要5秒才能重新连接到主服务器,那么复制积压缓冲区的大小不能小于5MB。为了安全起见,复制积压缓冲区的大小可以设置为2*second*write_size_per_second,这确保可以使用部分重新同步处理大多数断开连接。基于长连接的命令传播65哥:全量同步完成后,如何同步正常的运行过程?当主从库完成全量拷贝后,它们之间会保持一个网络连接,主库会使用这个连接依次接收数据。然后将接收到的命令操作同步到从库。这个过程也称为基于长连接的命令传播。使用长连接的目的是为了避免频繁建立连接带来的开销。在命令传播阶段,主从节点除了发送写命令外,还维护心跳机制:PING和REPLCONFACK。Master->Slave:PING每隔指定的时间,主节点会向从节点发送一个PING命令。这个PING命令的作用主要是让从节点做一个超时判断。Slave->Master:REPLCONFACK在命令传播阶段,从服务器默认每秒向主服务器发送一次命令:REPLCONFACK
