网站底部关键词内链,3分钟搞定网站seo优化外链建设,显卡服务器租用,网站广告位一般多少钱转载自 Redis进阶之主从复制
一、主从复制概述
主从复制#xff0c;是指将一台Redis服务器的数据#xff0c;复制到其他的Redis服务器。前者称为主节点(master)#xff0c;后者称为从节点(slave)#xff1b;数据的复制是单向的#xff0c;只能由主节点到从节点。
默认…转载自 Redis进阶之主从复制
一、主从复制概述
主从复制是指将一台Redis服务器的数据复制到其他的Redis服务器。前者称为主节点(master)后者称为从节点(slave)数据的复制是单向的只能由主节点到从节点。
默认情况下每台Redis服务器都是主节点且一个主节点可以有多个从节点(或没有从节点)但一个从节点只能有一个主节点。
主从复制的作用
主从复制的作用主要包括
1.数据冗余主从复制实现了数据的热备份是持久化之外的一种数据冗余方式。
2.故障恢复当主节点出现问题时可以由从节点提供服务实现快速的故障恢复实际上是一种服务的冗余。
3.负载均衡在主从复制的基础上配合读写分离可以由主节点提供写服务由从节点提供读服务即写Redis数据时应用连接主节点读Redis数据时应用连接从节点分担服务器负载尤其是在写少读多的场景下通过多个从节点分担读负载可以大大提高Redis服务器的并发量。
4.高可用基石除了上述作用以外主从复制还是哨兵和集群能够实施的基础因此说主从复制是Redis高可用的基础。 二、如何使用主从复制
为了更直观的理解主从复制在介绍其内部原理之前先说明我们需要如何操作才能开启主从复制。
1. 建立复制
需要注意主从复制的开启完全是在从节点发起的不需要我们在主节点做任何事情。
从节点开启主从复制有3种方式
1配置文件
在从服务器的配置文件中加入slaveof masterip masterport
2启动命令
redis-server启动命令后加入 --slaveof masterip masterport
3客户端命令
Redis服务器启动后直接通过客户端执行命令slaveof masterip masterport则该Redis实例成为从节点。
上述3种方式是等效的下面以客户端命令的方式为例看一下当执行了slaveof后Redis主节点和从节点的变化。
2. 实例
准备工作启动两个节点
方便起见实验所使用的主从节点是在一台机器上的不同Redis实例其中主节点监听6379端口从节点监听6380端口从节点监听的端口号可以在配置文件中修改 启动后可以看到 两个Redis节点启动后分别称为6379节点和6380节点默认都是主节点。
建立复制
此时在6380节点执行slaveof命令使之变为从节点 观察效果
下面验证一下在主从复制建立后主节点的数据会复制到从节点中。
1首先在从节点查询一个不存在的key 2然后在主节点中增加这个key 3此时在从节点中再次查询这个key会发现主节点的操作已经同步至从节点 4然后在主节点删除这个key 5此时在从节点中再次查询这个key会发现主节点的操作已经同步至从节点 3. 断开复制
通过slaveof masterip masterport命令建立主从复制关系以后可以通过slaveof no one断开。需要注意的是从节点断开复制后不会删除已有的数据只是不再接受主节点新的数据变化。
从节点执行slaveof no one后打印日志如下所示可以看出断开复制后从节点又变回为主节点。 主节点打印日志如下 三、主从复制的实现原理
上面一节中介绍了如何操作可以建立主从关系本小节将介绍主从复制的实现原理。
主从复制过程大体可以分为3个阶段连接建立阶段即准备阶段、数据同步阶段、命令传播阶段下面分别进行介绍。
1. 连接建立阶段
该阶段的主要作用是在主从节点之间建立连接为数据同步做好准备。
步骤1保存主节点信息
从节点服务器内部维护了两个字段即masterhost和masterport字段用于存储主节点的ip和port信息。
需要注意的是slaveof是异步命令从节点完成主节点ip和port的保存后向发送slaveof命令的客户端直接返回OK实际的复制操作在这之后才开始进行。
这个过程中可以看到从节点打印日志如下 步骤2建立socket连接
从节点每秒1次调用复制定时函数replicationCron()如果发现了有主节点可以连接便会根据主节点的ip和port创建socket连接。如果连接成功则
从节点为该socket建立一个专门处理复制工作的文件事件处理器负责后续的复制工作如接收RDB文件、接收命令传播等。
主节点接收到从节点的socket连接后即accept之后为该socket创建相应的客户端状态并将从节点看做是连接到主节点的一个客户端后面的步骤会以从节点向主节点发送命令请求的形式来进行。
这个过程中从节点打印日志如下 步骤3发送ping命令
从节点成为主节点的客户端之后发送ping命令进行首次请求目的是检查socket连接是否可用以及主节点当前是否能够处理请求。
从节点发送ping命令后可能出现3种情况
1返回pong说明socket连接正常且主节点当前可以处理请求复制过程继续。
2超时一定时间后从节点仍未收到主节点的回复说明socket连接不可用则从节点断开socket连接并重连。
3返回pong以外的结果如果主节点返回其他结果如正在处理超时运行的脚本说明主节点当前无法处理命令则从节点断开socket连接并重连。
在主节点返回pong情况下从节点打印日志如下 步骤4身份验证
如果从节点中设置了masterauth选项则从节点需要向主节点进行身份验证没有设置该选项则不需要验证。从节点进行身份验证是通过向主节点发送auth命令进行的auth命令的参数即为配置文件中的masterauth的值。
如果主节点设置密码的状态与从节点masterauth的状态一致一致是指都存在且密码相同或者都不存在则身份验证通过复制过程继续如果不一致则从节点断开socket连接并重连。
步骤5发送从节点端口信息
身份验证之后从节点会向主节点发送其监听的端口号前述例子中为6380主节点将该信息保存到该从节点对应的客户端的slave_listening_port字段中该端口信息除了在主节点中执行info Replication时显示以外没有其他作用。
2. 数据同步阶段
主从节点之间的连接建立以后便可以开始进行数据同步该阶段可以理解为从节点数据的初始化。具体执行的方式是从节点向主节点发送psync命令Redis2.8以前是sync命令开始同步。
数据同步阶段是主从复制最核心的阶段根据主从节点当前状态的不同可以分为全量复制和部分复制下面会有一章专门讲解这两种复制方式以及psync命令的执行过程这里不再详述。
需要注意的是在数据同步阶段之前从节点是主节点的客户端主节点不是从节点的客户端而到了这一阶段及以后主从节点互为客户端。原因在于在此之前主节点只需要响应从节点的请求即可不需要主动发请求而在数据同步阶段和后面的命令传播阶段主节点需要主动向从节点发送请求如推送缓冲区中的写命令才能完成复制。
3. 命令传播阶段
数据同步阶段完成后主从节点进入命令传播阶段在这个阶段主节点将自己执行的写命令发送给从节点从节点接收命令并执行从而保证主从节点数据的一致性。
在命令传播阶段除了发送写命令主从节点还维持着心跳机制PING和REPLCONF ACK。由于心跳机制的原理涉及部分复制因此将在介绍了部分复制的相关内容后单独介绍该心跳机制。
延迟与不一致
需要注意的是命令传播是异步的过程即主节点发送写命令后并不会等待从节点的回复因此实际上主从节点之间很难保持实时的一致性延迟在所难免。数据不一致的程度与主从节点之间的网络状况、主节点写命令的执行频率、以及主节点中的repl-disable-tcp-nodelay配置等有关。
repl-disable-tcp-nodelay no该配置作用于命令传播阶段控制主节点是否禁止与从节点的TCP_NODELAY默认no即不禁止TCP_NODELAY。当设置为yes时TCP会对包进行合并从而减少带宽但是发送的频率会降低从节点数据延迟增加一致性变差具体发送频率与Linux内核的配置有关默认配置为40ms。当设置为no时TCP会立马将主节点的数据发送给从节点带宽增加但延迟变小。
一般来说只有当应用对Redis数据不一致的容忍度较高且主从节点之间网络状况不好时才会设置为yes多数情况使用默认值no。
四、【数据同步阶段】全量复制和部分复制
在Redis2.8以前从节点向主节点发送sync命令请求同步数据此时的同步方式是全量复制在Redis2.8及以后从节点可以发送psync命令请求同步数据此时根据主从节点当前状态的不同同步方式可能是全量复制或部分复制。后文介绍以Redis2.8及以后版本为例。
1.全量复制用于初次复制或其他无法进行部分复制的情况将主节点中的所有数据都发送给从节点是一个非常重型的操作。
2.部分复制用于网络中断等情况后的复制只将中断期间主节点执行的写命令发送给从节点与全量复制相比更加高效。需要注意的是如果网络中断时间过长导致主节点没有能够完整地保存中断期间执行的写命令则无法进行部分复制仍使用全量复制。
1. 全量复制
Redis通过psync命令进行全量复制的过程如下
1从节点判断无法进行部分复制向主节点发送全量复制的请求或从节点发送部分复制的请求但主节点判断无法进行全量复制具体判断过程需要在讲述了部分复制原理后再介绍。
2主节点收到全量复制的命令后执行bgsave在后台生成RDB文件并使用一个缓冲区称为复制缓冲区记录从现在开始执行的所有写命令
3主节点的bgsave执行完成后将RDB文件发送给从节点从节点首先清除自己的旧数据然后载入接收的RDB文件将数据库状态更新至主节点执行bgsave时的数据库状态
4主节点将前述复制缓冲区中的所有写命令发送给从节点从节点执行这些写命令将数据库状态更新至主节点的最新状态
5如果从节点开启了AOF则会触发bgrewriteaof的执行从而保证AOF文件更新至主节点的最新状态
下面是执行全量复制时主从节点打印的日志可以看出日志内容与上述步骤是完全对应的。
主节点的打印日志如下 从节点打印日志如下图所示 其中有几点需要注意从节点接收了来自主节点的89260个字节的数据从节点在载入主节点的数据之前要先将老数据清除从节点在同步完数据后调用了bgrewriteaof。
通过全量复制的过程可以看出全量复制是非常重型的操作
1主节点通过bgsave命令fork子进程进行RDB持久化该过程是非常消耗CPU、内存(页表复制)、硬盘IO的关于bgsave的性能问题可以参考深入学习Redis进阶之持久化。
2主节点通过网络将RDB文件发送给从节点对主从节点的带宽都会带来很大的消耗
3从节点清空老数据、载入新RDB文件的过程是阻塞的无法响应客户端的命令如果从节点执行bgrewriteaof也会带来额外的消耗 2. 部分复制
由于全量复制在主节点数据量较大时效率太低因此Redis2.8开始提供部分复制用于处理网络中断时的数据同步。
部分复制的实现依赖于三个重要的概念
1复制偏移量
主节点和从节点分别维护一个复制偏移量offset代表的是主节点向从节点传递的字节数主节点每次向从节点传播N个字节数据时主节点的offset增加N从节点每次收到主节点传来的N个字节数据时从节点的offset增加N。
offset用于判断主从节点的数据库状态是否一致如果二者offset相同则一致如果offset不同则不一致此时可以根据两个offset找出从节点缺少的那部分数据。例如如果主节点的offset是1000而从节点的offset是500那么部分复制就需要将offset为501-1000的数据传递给从节点。而offset为501-1000的数据存储的位置就是下面要介绍的复制积压缓冲区。
2复制积压缓冲区
复制积压缓冲区是由主节点维护的、固定长度的、先进先出(FIFO)队列默认大小1MB当主节点开始有从节点时创建其作用是备份主节点最近发送给从节点的数据。注意无论主节点有一个还是多个从节点都只需要一个复制积压缓冲区。
在命令传播阶段主节点除了将写命令发送给从节点还会发送一份给复制积压缓冲区作为写命令的备份除了存储写命令复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量offset。由于复制积压缓冲区定长且是先进先出所以它保存的是主节点最近执行的写命令时间较早的写命令会被挤出缓冲区。
由于该缓冲区长度固定且有限因此可以备份的写命令也有限当主从节点offset的差距过大超过缓冲区长度时将无法执行部分复制只能执行全量复制。反过来说为了提高网络中断时部分复制执行的概率可以根据需要增大复制积压缓冲区的大小(通过配置repl-backlog-size)例如如果网络中断的平均时间是60s而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB则复制积压缓冲区的平均需求为6MB保险起见可以设置为12MB来保证绝大多数断线情况都可以使用部分复制。
从节点将offset发送给主节点后主节点根据offset和缓冲区大小决定能否执行部分复制 如果offset偏移量之后的数据仍然都在复制积压缓冲区里则执行部分复制 如果offset偏移量之后的数据已不在复制积压缓冲区中数据已被挤出则执行全量复制。
3服务器运行ID(runid)
每个Redis节点(无论主从)在启动时都会自动生成一个随机ID(每次启动都不一样)由40个随机的十六进制字符组成runid用来唯一识别一个Redis节点。通过info Server命令可以查看节点的runid 主从节点初次复制时主节点将自己的runid发送给从节点从节点将这个runid保存起来当断线重连时从节点会将这个runid发送给主节点主节点根据runid判断能否进行部分复制 如果从节点保存的runid与主节点现在的runid相同说明主从节点之前同步过主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况) 如果从节点保存的runid与主节点现在的runid不同说明从节点在断线前同步的Redis节点并不是当前的主节点只能进行全量复制。
3. psync命令的执行
在了解了复制偏移量、复制积压缓冲区、节点运行id之后本节将介绍psync命令的参数和返回值从而说明psync命令执行过程中主从节点是如何确定使用全量复制还是部分复制的。
psync命令的执行过程可以参见下图图片来源《Redis设计与实现》 1首先从节点根据当前状态决定如何调用psync命令 如果从节点之前未执行过slaveof或最近执行了slaveof no one则从节点发送命令为psync ? -1向主节点请求全量复制 如果从节点之前执行了slaveof则发送命令为psync runid offset其中runid为上次复制的主节点的runidoffset为上次复制截止时从节点保存的复制偏移量。
2主节点根据收到的psync命令及当前服务器状态决定执行全量复制还是部分复制 如果主节点版本低于Redis2.8则返回-ERR回复此时从节点重新发送sync命令执行全量复制 如果主节点版本够新且runid与从节点发送的runid相同且从节点发送的offset之后的数据在复制积压缓冲区中都存在则回复CONTINUE表示将进行部分复制从节点等待主节点发送其缺少的数据即可 如果主节点版本够新但是runid与从节点发送的runid不同或从节点发送的offset之后的数据已不在复制积压缓冲区中(在队列中被挤出了)则回复FULLRESYNC runid offset表示要进行全量复制其中runid表示主节点当前的runidoffset表示主节点当前的offset从节点保存这两个值以备使用。
4. 部分复制演示
在下面的演示中网络中断几分钟后恢复断开连接的主从节点进行了部分复制为了便于模拟网络中断本例中的主从节点在局域网中的两台机器上。
网络中断
网络中断一段时间后主节点和从节点都会发现失去了与对方的连接关于主从节点对超时的判断机制后面会有说明此后从节点便开始执行对主节点的重连由于此时网络还没有恢复重连失败从节点会一直尝试重连。
主节点日志如下 从节点日志如下 网络恢复
网络恢复后从节点连接主节点成功并请求进行部分复制主节点接收请求后二者进行部分复制以同步数据。
主节点日志如下 从节点日志如下 五、【命令传播阶段】心跳机制
在命令传播阶段除了发送写命令主从节点还维持着心跳机制PING和REPLCONF ACK。心跳机制对于主从复制的超时判断、数据安全等有作用。
1.主-从PING
每隔指定的时间主节点会向从节点发送PING命令这个PING命令的作用主要是为了让从节点进行超时判断。
PING发送的频率由repl-ping-slave-period参数控制单位是秒默认值是10s。
关于该PING命令究竟是由主节点发给从节点还是相反有一些争议因为在Redis的官方文档中对该参数的注释中说明是从节点向主节点发送PING命令如下图所示 但是根据该参数的名称(含有ping-slave)以及代码实现我认为该PING命令是主节点发给从节点的。相关代码如下 2. 从-主REPLCONF ACK
在命令传播阶段从节点会向主节点发送REPLCONF ACK命令频率是每秒1次命令格式为REPLCONF ACK {offset}其中offset指从节点保存的复制偏移量。REPLCONF ACK命令的作用包括
1实时监测主从节点网络状态该命令会被主节点用于复制超时的判断。此外在主节点中使用info Replication可以看到其从节点的状态中的lag值代表的是主节点上次收到该REPLCONF ACK命令的时间间隔在正常情况下该值应该是0或1如下图所示 2检测命令丢失从节点发送了自身的offset主节点会与自己的offset对比如果从节点数据缺失如网络丢包主节点会推送缺失的数据这里也会利用复制积压缓冲区。注意offset和复制积压缓冲区不仅可以用于部分复制也可以用于处理命令丢失等情形区别在于前者是在断线重连后进行的而后者是在主从节点没有断线的情况下进行的。
3辅助保证从节点的数量和延迟Redis主节点中使用min-slaves-to-write和min-slaves-max-lag参数来保证主节点在不安全的情况下不会执行写命令所谓不安全是指从节点数量太少或延迟过高。例如min-slaves-to-write和min-slaves-max-lag分别是3和10含义是如果从节点数量小于3个或所有从节点的延迟值都大于10s则主节点拒绝执行写命令。而这里从节点延迟值的获取就是通过主节点接收到REPLCONF ACK命令的时间来判断的即前面所说的info Replication中的lag值。
六、应用中的问题
1. 读写分离及其中的问题
在主从复制基础上实现的读写分离可以实现Redis的读负载均衡由主节点提供写服务由一个或多个从节点提供读服务多个从节点既可以提高数据冗余程度也可以最大化读负载能力在读负载较大的应用场景下可以大大提高Redis服务器的并发量。下面介绍在使用Redis读写分离时需要注意的问题。
1延迟与不一致问题
前面已经讲到由于主从复制的命令传播是异步的延迟与数据的不一致不可避免。如果应用对数据不一致的接受程度程度较低可能的优化措施包括优化主从节点之间的网络环境如在同机房部署监控主从节点延迟通过offset判断如果从节点延迟过大通知应用不再通过该从节点读取数据使用集群同时扩展写负载和读负载等。
在命令传播阶段以外的其他情况下从节点的数据不一致可能更加严重例如连接在数据同步阶段或从节点失去与主节点的连接时等。从节点的slave-serve-stale-data参数便与此有关它控制这种情况下从节点的表现如果为yes默认值则从节点仍能够响应客户端的命令如果为no则从节点只能响应info、slaveof等少数命令。该参数的设置与应用对数据一致性的要求有关如果对数据一致性要求很高则应设置为no。
2数据过期问题
在单机版Redis中存在两种删除策略 惰性删除服务器不会主动删除数据只有当客户端查询某个数据时服务器判断该数据是否过期如果过期则删除。 定期删除服务器执行定时任务删除过期数据但是考虑到内存和CPU的折中删除会释放内存但是频繁的删除操作对CPU不友好该删除的频率和执行时间都受到了限制。
在主从复制场景下为了主从节点的数据一致性从节点不会主动删除数据而是由主节点控制从节点中过期数据的删除。由于主节点的惰性删除和定期删除策略都不能保证主节点及时对过期数据执行删除操作因此当客户端通过Redis从节点读取数据时很容易读取到已经过期的数据。
Redis 3.2中从节点在读取数据时增加了对数据是否过期的判断如果该数据已过期则不返回给客户端将Redis升级到3.2可以解决数据过期问题。
3故障切换问题
在没有使用哨兵的读写分离场景下应用针对读和写分别连接不同的Redis节点当主节点或从节点出现问题而发生更改时需要及时修改应用程序读写Redis数据的连接连接的切换可以手动进行或者自己写监控程序进行切换但前者响应慢、容易出错后者实现复杂成本都不算低。
4总结
在使用读写分离之前可以考虑其他方法增加Redis的读负载能力如尽量优化主节点减少慢查询、减少持久化等其他情况带来的阻塞等提高负载能力使用Redis集群同时提高读负载能力和写负载能力等。如果使用读写分离可以使用哨兵使主从节点的故障切换尽可能自动化并减少对应用程序的侵入。
2. 复制超时问题
主从节点复制超时是导致复制中断的最重要的原因之一本小节单独说明超时问题下一小节说明其他会导致复制中断的问题。
超时判断意义
在复制连接建立过程中及之后主从节点都有机制判断连接是否超时其意义在于
1如果主节点判断连接超时其会释放相应从节点的连接从而释放各种资源否则无效的从节点仍会占用主节点的各种资源输出缓冲区、带宽、连接等此外连接超时的判断可以让主节点更准确的知道当前有效从节点的个数有助于保证数据安全配合前面讲到的min-slaves-to-write等参数。
2如果从节点判断连接超时则可以及时重新建立连接避免与主节点数据长期的不一致。
判断机制
主从复制超时判断的核心在于repl-timeout参数该参数规定了超时时间的阈值默认60s对于主节点和从节点同时有效主从节点触发超时的条件分别如下
1主节点每秒1次调用复制定时函数replicationCron()在其中判断当前时间距离上次收到各个从节点REPLCONF ACK的时间是否超过了repl-timeout值如果超过了则释放相应从节点的连接。
2从节点从节点对超时的判断同样是在复制定时函数中判断基本逻辑是 如果当前处于连接建立阶段且距离上次收到主节点的信息的时间已超过repl-timeout则释放与主节点的连接 如果当前处于数据同步阶段且收到主节点的RDB文件的时间超时则停止数据同步释放连接 如果当前处于命令传播阶段且距离上次收到主节点的PING命令或数据的时间已超过repl-timeout值则释放与主节点的连接。 主从节点判断连接超时的相关源代码如下
/* Replication cron function, called 1 time per second. */
void replicationCron(void) {
static long long replication_cron_loops 0;/* Non blocking connection timeout? */
if (server.masterhost (server.repl_state REDIS_REPL_CONNECTING ||slaveIsInHandshakeState()) (time(NULL)-server.repl_transfer_lastio) server.repl_timeout)
{redisLog(REDIS_WARNING,Timeout connecting to the MASTER...);undoConnectWithMaster();
}/* Bulk transfer I/O timeout? */
if (server.masterhost server.repl_state REDIS_REPL_TRANSFER (time(NULL)-server.repl_transfer_lastio) server.repl_timeout)
{redisLog(REDIS_WARNING,Timeout receiving bulk data from MASTER... If the problem persists try to set the repl-timeout parameter in redis.conf to a larger value.);replicationAbortSyncTransfer();
}/* Timed out master when we are an already connected slave? */
if (server.masterhost server.repl_state REDIS_REPL_CONNECTED (time(NULL)-server.master-lastinteraction) server.repl_timeout)
{redisLog(REDIS_WARNING,MASTER timeout: no data nor PING received...);freeClient(server.master);
}//此处省略无关代码……/* Disconnect timedout slaves. */
if (listLength(server.slaves)) {listIter li;listNode *ln;listRewind(server.slaves,li);while((ln listNext(li))) {redisClient *slave ln-value;if (slave-replstate ! REDIS_REPL_ONLINE) continue;if (slave-flags REDIS_PRE_PSYNC) continue;if ((server.unixtime - slave-repl_ack_time) server.repl_timeout){redisLog(REDIS_WARNING, Disconnecting timedout slave: %s,replicationGetSlaveName(slave));freeClient(slave);}}
}//此处省略无关代码……}
需要注意的坑
下面介绍与复制阶段连接超时有关的一些实际问题
1数据同步阶段在主从节点进行全量复制bgsave时主节点需要首先fork子进程将当前数据保存到RDB文件中然后再将RDB文件通过网络传输到从节点。如果RDB文件过大主节点在fork子进程保存RDB文件时耗时过多可能会导致从节点长时间收不到数据而触发超时此时从节点会重连主节点然后再次全量复制再次超时再次重连……这是个悲伤的循环。为了避免这种情况的发生除了注意Redis单机数据量不要过大另一方面就是适当增大repl-timeout值具体的大小可以根据bgsave耗时来调整。
2命令传播阶段如前所述在该阶段主节点会向从节点发送PING命令频率由repl-ping-slave-period控制该参数应明显小于repl-timeout值(后者至少是前者的几倍)。否则如果两个参数相等或接近网络抖动导致个别PING命令丢失此时恰巧主节点也没有向从节点发送数据则从节点很容易判断超时。
3慢查询导致的阻塞如果主节点或从节点执行了一些慢查询如keys *或者对大数据的hgetall等导致服务器阻塞阻塞期间无法响应复制连接中对方节点的请求可能导致复制超时。
3. 复制中断问题
主从节点超时是复制中断的原因之一除此之外还有其他情况可能导致复制中断其中最主要的是复制缓冲区溢出问题。
复制缓冲区溢出
前面曾提到过在全量复制阶段主节点会将执行的写命令放到复制缓冲区中该缓冲区存放的数据包括了以下几个时间段内主节点执行的写命令bgsave生成RDB文件、RDB文件由主节点发往从节点、从节点清空老数据并载入RDB文件中的数据。当主节点数据量较大或者主从节点之间网络延迟较大时可能导致该缓冲区的大小超过了限制此时主节点会断开与从节点之间的连接这种情况可能引起全量复制-复制缓冲区溢出导致连接中断-重连-全量复制-复制缓冲区溢出导致连接中断……的循环。
复制缓冲区的大小由client-output-buffer-limit slave {hard limit} {soft limit} {soft seconds}配置默认值为client-output-buffer-limit slave 256MB 64MB 60其含义是如果buffer大于256MB或者连续60s大于64MB则主节点会断开与该从节点的连接。该参数是可以通过config set命令动态配置的即不重启Redis也可以生效。
当复制缓冲区溢出时主节点打印日志如下所示 需要注意的是复制缓冲区是客户端输出缓冲区的一种主节点会为每一个从节点分别分配复制缓冲区而复制积压缓冲区则是一个主节点只有一个无论它有多少个从节点。
4. 各场景下复制的选择及优化技巧
在介绍了Redis复制的种种细节之后现在我们可以来总结一下在下面常见的场景中何时使用部分复制以及需要注意哪些问题。
1第一次建立复制
此时全量复制不可避免但仍有几点需要注意如果主节点的数据量较大应该尽量避开流量的高峰期避免造成阻塞如果有多个从节点需要建立对主节点的复制可以考虑将几个从节点错开避免主节点带宽占用过大。此外如果从节点过多也可以调整主从复制的拓扑结构由一主多从结构变为树状结构中间的节点既是其主节点的从节点也是其从节点的主节点但使用树状结构应该谨慎虽然主节点的直接从节点减少降低了主节点的负担但是多层从节点的延迟增大数据一致性变差且结构复杂维护相当困难。
2主节点重启
主节点重启可以分为两种情况来讨论一种是故障导致宕机另一种则是有计划的重启。
主节点宕机
主节点宕机重启后runid会发生变化因此不能进行部分复制只能全量复制。
实际上在主节点宕机的情况下应进行故障转移处理将其中的一个从节点升级为主节点其他从节点从新的主节点进行复制且故障转移应尽量的自动化后面文章将要介绍的哨兵便可以进行自动的故障转移。
安全重启debug reload
在一些场景下可能希望对主节点进行重启例如主节点内存碎片率过高或者希望调整一些只能在启动时调整的参数。如果使用普通的手段重启主节点会使得runid发生变化可能导致不必要的全量复制。
为了解决这个问题Redis提供了debug reload的重启方式重启后主节点的runid和offset都不受影响避免了全量复制。
如下图所示debug reload重启后runid和offset都未受影响 但debug reload是一柄双刃剑它会清空当前内存中的数据重新从RDB文件中加载这个过程会导致主节点的阻塞因此也需要谨慎。
3从节点重启
从节点宕机重启后其保存的主节点的runid会丢失因此即使再次执行slaveof也无法进行部分复制。
4网络中断
如果主从节点之间出现网络问题造成短时间内网络中断可以分为多种情况讨论。
第一种情况网络问题时间极为短暂只造成了短暂的丢包主从节点都没有判定超时未触发repl-timeout此时只需要通过REPLCONF ACK来补充丢失的数据即可。
第二种情况网络问题时间很长主从节点判断超时触发了repl-timeout且丢失的数据过多超过了复制积压缓冲区所能存储的范围此时主从节点无法进行部分复制只能进行全量复制。为了尽可能避免这种情况的发生应该根据实际情况适当调整复制积压缓冲区的大小此外及时发现并修复网络中断也可以减少全量复制。
第三种情况介于前述两种情况之间主从节点判断超时且丢失的数据仍然都在复制积压缓冲区中此时主从节点可以进行部分复制。
5. 复制相关的配置
这一节总结一下与复制有关的配置说明这些配置的作用、起作用的阶段以及配置方法等通过了解这些配置一方面加深对Redis复制的了解另一方面掌握这些配置的方法可以优化Redis的使用少走坑。
配置大致可以分为主节点相关配置、从节点相关配置以及与主从节点都有关的配置下面分别说明。
1与主从节点都有关的配置
首先介绍最特殊的配置它决定了该节点是主节点还是从节点
1) slaveof masterip masterportRedis启动时起作用作用是建立复制关系开启了该配置的Redis服务器在启动后成为从节点。该注释默认注释掉即Redis服务器默认都是主节点。
2) repl-timeout 60与各个阶段主从节点连接超时判断有关见前面的介绍。
2主节点相关配置
1) repl-diskless-sync no作用于全量复制阶段控制主节点是否使用diskless复制无盘复制。所谓diskless复制是指在全量复制时主节点不再先把数据写入RDB文件而是直接写入slave的socket中整个过程中不涉及硬盘diskless复制在磁盘IO很慢而网速很快时更有优势。需要注意的是截至Redis3.0diskless复制处于实验阶段默认是关闭的。
2) repl-diskless-sync-delay 5该配置作用于全量复制阶段当主节点使用diskless复制时该配置决定主节点向从节点发送之前停顿的时间单位是秒只有当diskless复制打开时有效默认5s。之所以设置停顿时间是基于以下两个考虑(1)向slave的socket的传输一旦开始新连接的slave只能等待当前数据传输结束才能开始新的数据传输 (2)多个从节点有较大的概率在短时间内建立主从复制。
3) client-output-buffer-limit slave 256MB 64MB 60与全量复制阶段主节点的缓冲区大小有关见前面的介绍。
4) repl-disable-tcp-nodelay no与命令传播阶段的延迟有关见前面的介绍。
5) masterauth master-password与连接建立阶段的身份验证有关见前面的介绍。
6) repl-ping-slave-period 10与命令传播阶段主从节点的超时判断有关见前面的介绍。
7) repl-backlog-size 1mb复制积压缓冲区的大小见前面的介绍。
8) repl-backlog-ttl 3600当主节点没有从节点时复制积压缓冲区保留的时间这样当断开的从节点重新连进来时可以进行全量复制默认3600s。如果设置为0则永远不会释放复制积压缓冲区。
9) min-slaves-to-write 3与min-slaves-max-lag 10规定了主节点的最小从节点数目及对应的最大延迟见前面的介绍。
3从节点相关配置
1) slave-serve-stale-data yes与从节点数据陈旧时是否响应客户端命令有关见前面的介绍。
2) slave-read-only yes从节点是否只读默认是只读的。由于从节点开启写操作容易导致主从节点的数据不一致因此该配置尽量不要修改。
6. 单机内存大小限制
在Redis进阶之持久化一文中讲到了fork操作对Redis单机内存大小的限制。实际上在Redis的使用中限制单机内存大小的因素非常之多下面总结一下在主从复制中单机内存过大可能造成的影响
1切主当主节点宕机时一种常见的容灾策略是将其中一个从节点提升为主节点并将其他从节点挂载到新的主节点上此时这些从节点只能进行全量复制如果Redis单机内存达到10GB一个从节点的同步时间在几分钟的级别如果从节点较多恢复的速度会更慢。如果系统的读负载很高而这段时间从节点无法提供服务会对系统造成很大的压力。
2从库扩容如果访问量突然增大此时希望增加从节点分担读负载如果数据量过大从节点同步太慢难以及时应对访问量的暴增。
3缓冲区溢出1和2都是从节点可以正常同步的情形虽然慢但是如果数据量过大导致全量复制阶段主节点的复制缓冲区溢出从而导致复制中断则主从节点的数据同步会全量复制-复制缓冲区溢出导致复制中断-重连-全量复制-复制缓冲区溢出导致复制中断……的循环。
4超时如果数据量过大全量复制阶段主节点fork保存RDB文件耗时过大从节点长时间接收不到数据触发超时主从节点的数据同步同样可能陷入全量复制-超时导致复制中断-重连-全量复制-超时导致复制中断……的循环。
此外主节点单机内存除了绝对量不能太大其占用主机内存的比例也不应过大最好只使用50%-65%的内存留下30%-45%的内存用于执行bgsave命令和创建复制缓冲区等。
7. info Replication
在Redis客户端通过info Replication可以查看与复制相关的状态对于了解主从节点的当前状态以及解决出现的问题都会有帮助。
主节点 从节点 对于从节点上半部分展示的是其作为从节点的状态从connectd_slaves开始展示的是其作为潜在的主节点的状态。
info Replication中展示的大部分内容在文章中都已经讲述这里不再详述。
七、总结
下面回顾一下本文的主要内容
1、主从复制的作用宏观的了解主从复制是为了解决什么样的问题即数据冗余、故障恢复、读负载均衡等。
2、主从复制的操作即slaveof命令。
3、主从复制的原理主从复制包括了连接建立阶段、数据同步阶段、命令传播阶段其中数据同步阶段有全量复制和部分复制两种数据同步方式命令传播阶段主从节点之间有PING和REPLCONF ACK命令互相进行心跳检测。
4、应用中的问题包括读写分离的问题数据不一致问题、数据过期问题、故障切换问题等、复制超时问题、复制中断问题等然后总结了主从复制相关的配置其中repl-timeout、client-output-buffer-limit slave等对解决Redis主从复制中出现的问题可能会有帮助。
主从复制虽然解决或缓解了数据冗余、故障恢复、读负载均衡等问题但其缺陷仍很明显故障恢复无法自动化写操作无法负载均衡存储能力受到单机的限制这些问题的解决需要哨兵和集群的帮助我将在后面的文章中介绍欢迎关注。