Redis详解(4)- 主从复制原理

一、概述


Master节点在平时提供服务,另一个或多个Slave节点在平时不提供服务(或只提供数据读取服务)。当Master节点因为某些缘由中止服务后,再人工/自动完成Slave节点到Master节点的切换工做,以便整个Redis集群继续向外提供服务。node

 

二、主从复制工做过程


Redis的主从复制功能除了支持一个Master节点对应多个Slave节点的同时进行复制外,还支持Slave节点向其它多个Slave节点进行复制。这样使得咱们可以灵活组织业务缓存数据的传播,例如使用多个Slave做为数据读取服务的同时,专门使用一个Slave节点为流式分析工具服务。Redis的主从复制功能分为两种数据同步模式进行:全量数据同步和增量数据同步。redis

全量数据同步:算法

先执行一次全同步 — 请求master BgSave出本身的一个RDB Snapshot文件发给slave,slave接收完毕后,清除掉本身的旧数据,而后将RDB载入内存。缓存

增量数据同步:
再进行增量同步 — master做为一个普通的client连入slave,将全部写操做转发给slave,没有特殊的同步协议。服务器

 

上图简要说明了Redis中Master节点到Slave节点的全量数据同步过程。当Slave节点给定的run_id和Master的run_id不一致时,或者Slave给定的上一次增量同步的offset的位置在Master的环形内存中没法定位时(后文会提到),Master就会对Slave发起全量同步操做。这时不管您是否在Master打开了RDB快照功能,它和Slave节点的每一次全量同步操做过程都会更新/建立Master上的RDB文件。在Slave链接到Master,并完成第一次全量数据同步后,接下来Master到Slave的数据同步过程通常就是增量同步形式了(也称为部分同步)。增量同步过程再也不主要依赖RDB文件,Master会将新产生的数据变化操做存放在一个内存区域,这个内存区域采用环形构造。过程以下网络

 

为何在Master上新增的数据除了根据Master节点上RDB或者AOF的设置进行日志文件更新外,还会同时将数据变化写入一个环形内存结构,并之后者为依据进行Slave节点的增量更新呢?主要缘由有如下几个:less

  • 因为网络环境的不稳定,网络抖动/延迟均可能形成Slave和Master暂时断开链接,这种状况要远远多于新的Slave链接到Master的状况。若是以上全部状况都使用全量更新,就会大大增长Master的负载压力——写RDB文件是有大量I/O过程的,虽然Linux Page Cahe特性会减小性能消耗。tcp

  • 另外在数据量达到必定规模的状况下,使用全量更新进行和Slave的第一次同步是一个不得已的选择——由于要尽快减小Slave节点和Master节点的数据差别。因此只能占用Master节点的资源和网络带宽资源。工具

  • 使用内存记录数据增量操做,能够有效减小Master节点在这方面付出的I/O代价。而作成环形内存的缘由,是为了保证在知足数据记录需求的状况下尽量减小内存的占用量。这个环形内存的大小,能够经过repl-backlog-size参数进行设置。性能

Slave重连后会向Master发送以前接收到的Master run_id信息和上一次完成部分同步的offset的位置信息。若是Master可以肯定这个run_id和本身的run_id一致且可以在环形内存中找到这个offset的位置,Master就会发送从offset的位置开始向Slave发送增量数据。那么链接正常的各个Slave节点如何接受新数据呢?链接正常的Slave节点将会在Master节点将数据写入环形内存后,主动接收到来自Master的数据复制信息。

 

三、主从复制配置


slave能够在配置文件、启动命令行参数、以及redis-cli执行SlaveOf指令来设置本身是slave。
测试代表同步延时很是小,指令一旦执行完毕就会马上写AOF文件和向Slave转发,除非Slave本身被阻塞住了。
比较蠢的是,即便在配置文件里设了slavof,slave启动时依然会先从数据文件载入一堆没用的数据,再去执行slaveof。
“Slaveof no one”,立马变身master。
2.8 版本将支持PSYNC部分同步,master会拨出一小段内存来存放要发给slave的指令,若是slave短暂的断开了,重连时会从内存中读取须要补读 的指令,这样就不须要断开两秒也搞一次全同步了。但若是断开时间较长,已经超过了内存中保存的数据,就仍是要全同步。
Slave也能够接收Read-Only的请求。

Redis提供的主从复制功能的配置信息,在Redis主配置文件的“REPLICATION”部分。如下是这个部分的主要参数项说明:

  • slaveof <masterip> <masterport>:若是您须要将某个节点设置为某个Master节点的Slave节点,您须要在这里指定Master节点的IP信息和端口信息。这个设置项默认是关闭的,也便是说Master节点不须要设置这个参数。另外,除了经过配置文件设置外,您还能够经过Redis的客户端命令进行slaveof设定。

  • slave-serve-stale-data:当master节点断开和当前salve节点的链接或者当前slave节点正在进行和master节点的数据同步时,若是收到了客户端的数据读取请求,slave服务器是否使用陈旧数据向客户端提供服务。该参数的默认值为yes。

  • slave-read-only 是否将salve节点设置为“只读”。一旦设置为“只读”,表示这个Salve节点只会进行数据读取服务,若是客户端直接向这个Salve节点发送写数据的请求,则会收到错误提示。建议采用默认的“yes”值进行设定。

  • repl-diskless-sync:上文已经介绍过Redis的主从复制功能基于RDB,后者的过程是将数据刷入RDB文件(其实是Linux的Page Cache区域),而后基于RDB文件内容的更新状况和Salve当前已同步的数据标记点来进行Salve上的数据更新。因此这个过程实际会增长必定的数据延迟,消耗必定的处理资源。基于这个状况,Redis中提供了一种不通过物理磁盘设备就进行主从数据同步的技术,称为diskless。可是直到Redis version 3.2这个技术也一直处于试验状态,因此并不推荐在生产环境下使用:“ 
    WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY”。

  • repl-diskless-sync-delay:这个参数只有在上一个参数设置为“yes”时才起做用,主要是设置在进行两次diskless模式的数据同步操做的时间间隔。默认为5秒。

  • repl-ping-slave-period:Slave节点向Master节点发送ping指令的事件间隔,默认为10秒。

  • repl-timeout:这是一个超时间,当某些操做达到这个时间时,Master和Slave双方都会认为对方已经断开链接。实际上您能够将这个时间当作是一个租约到期的时间。那么这个操做时间会影响哪些操做呢?A、向Slave进行的数据同步操做自己不能超过这个时间;B、Slave向Master发送一个PING指令并等待响应的时间;C、Master向Slave发送PONG回复并等待ACK的时间。

  • repl-disable-tcp-nodelay:这个选项的默认值为no,它对优化主从复制时使用的网络资源很是有用。要明白这个参数的含义,就首先要解释一下tcp-nodelay是个什么玩意儿?TCP数据报的报文头包含不少属性,这些属性基本上起到记录和保证传输目的、传输状态的做用,但没有数据报的所携带的业务数据(称之为有效载荷)。那么很明显,20个字节内容的信息分红20个数据报进行传输和只用一个数据报进行传输,须要占用的网络资源就彻底不同。JohnNagle在1984年发明了一种减轻网络传输压力的算法,就是为了解决这个问题(算法的名字就叫作“Nagle”,后续的技术人员又作了不少改进和升级)。其基本思路就是将要发送的内容凑够必定的数量后,再用一个数据报发送出去。若是该属性设置为yes,Redis将使用“Nagle”算法(或相似算法),让数据报中的有效载荷凑够必定数量后,在发送出去;设置成no,Redis就不会这么作。

  • repl-backlog-size:上文已经介绍过了Redis中为了进行增量同步所准备的环形内存区域,以及Redis这样作的缘由额,因此这里就再也不赘述了。这个选项就是用来设置环形内存的大小的,这个选项的默认值为1MB;正式的生产环境下能够稍微加大一些,例如5MB。

  • slave-priority:当前Slave节点的优先级权重。咱们后文会介绍一款Redis自带的监控和故障转移工具:Redis Sentinel,这个工具容许一个Master节点下有多个Slave节点,而且能够自动切换Slave节点为Master节点。若是Slave节点的优先级权重值越低,就会再切换时有限成为新的Master节点。

  • min-slaves-to-write和min-slaves-max-lag:为了尽量避免Master节点对应的多个Slave节点在数据复制过程当中数据差别被越拉越大。Redis服务提供了一组拒绝数据写操做的策略,这个策略能够解释为:当Master上在min-slaves-max-lag时间(单位秒)间隔后,任然有min-slaves-to-write个Slave和它正常链接,那么Master才容许进行数据写操做。

一、主库master配置:

Master服务器不须要针对主从复制作任何的设置(这不包括对主从复制过程的配置优化)。

二、从库slave配置:

Slave节点上咱们只须要作一件事情,就是打开slaveof选项:

# slaveof选项的设置,给定master节点的ip和port就能够了

#192.168.61.140就是master节点

slaveof 192.168.10.10 6379

接着,咱们立刻就能够看看同步效果了。首先确保您的master节点使工做正常的,而后就能够启动Slave节点了

 

三、Redis-sentinel- Fail-over


Redis-sentinel是2.6版开始加入的另外一组独立运行的节点,提供自动Fail Over的支持。
 

1 、主要执行过程

Sentinel每秒钟对全部master,slave和其余sentinel执行Ping,redis-server节点要应答+PONG或-LOADING或-MASTERDOWN.
若是某一台Sentinel没有在30秒内(可配置得短一些哦)收到上述正确应答,它就会认为master处于sdown状态(主观Down)
它向其余sentinel询问是否也认为该master倒了(SENTINEL is-master-down-by-addr ), 若是quonum台(默认是2)sentinel在5秒钟内都这样认为,就会认为master真是odown了(客观Down)。
此时会选出一台sentinel做为Leader执行fail-over, Leader会从slave中选出一个提高为master(执行slaveof no one),而后让其余slave指向它(执行slaveof new master)。

二、 master/slave 及 其余sentinel的发现

master 地址在sentinel.conf里, sentinel会每10秒一次向master发送INFO,知道master的slave有哪些。 若是master已经变为slave,sentinel会分析INFO的应答指向新的master。之前,sentinel重启时,若是master已经 切换过了,但sentinel.conf里master的地址并无变,极可能有悲剧发生。另外master重启后若是没有切换成slave,也可能有悲 剧发生。新版好像修复了一点这个问题,待研究。

另 外,sentinel会在master上建一个pub/sub channel,名为”sentinel:hello”,通告各类信息,sentinel们也是经过接收pub/sub channel上的+sentinel的信息发现彼此,由于每台sentinel每5秒会发送一次本身的host信息,宣告本身的存在。

 

三、自定义reconfig脚本

sentinel在failover时还会执行配置文件里指定的用户自定义reconfig脚本,作用户本身想作的事情,好比让master变为slave并指向新的master。
脚 本的将会在命令行按顺序传入以下参数: <master-name> <role(leader/observer)> <state(上述三种状况)> <from-ip> <from-port> <to-ip> <to-port>
脚本返回0是正常,若是返回1会被从新执行,若是返回2或以上不会。 若是超过60秒没返回会被强制终止。
以为Sentinel至少有两个可提高的地方:

一是若是master 主动shutdown,好比系统升级,有办法主动通知sentinel提高新的master,减小服务中断时间。
二是比起redis-server太原始了,要本身丑陋的以nohup sentinel > logfile 2>&1 & 启动,也不支持shutdown命令,要本身kill pid。

 

四、Client的高可用性

基 于Sentinel的方案,client须要执行语句SENTINEL get-master-addr-by-name mymaster 可得到当前master的地址。 Jedis正在集成sentinel,已经支持了sentinel的一些指令,但还没发布,但sentinel版的链接池则暂时彻底没有,在公司的项目里 我参考网友的项目本身写了一个。

淘 宝的Tedis driver,使用了彻底不一样的思路,不基于Sentinel,而是多写随机读, 一开始就同步写入到全部节点,读的话随便读一个还活着的节点就好了。但有些节点成功有些节点失败如何处理? 节点死掉从新起来后怎么从新同步?何时能够从新Ready? 因此不是很敢用。

另外如Ruby写的redis_failover,也是抛开了Redis Sentinel,基于ZooKeeper的临时方案。

Redis做者也在博客里抱怨怎么没有人作Dynamo-style 的client。

 

四、问题


 

一、 Trouble Shooting again

有时候明明master/slave都活得好好的,忽然间就说要从新进行全同步了:

1.Slave显示:# MASTER time out: no data nor PING received…

slave 会每隔repl-ping-slave-period(默认10秒)ping一次master,若是超过repl-timeout(默认60秒)都没有收 到响应,就会认为Master挂了。若是Master明明没挂但被阻塞住了也会报这个错。能够适当调大repl-timeout。

2.Master 显示:# Client addr=10.175.162.123:44670 flags=S oll=104654 omem=2147487792 events=rw cmd=sync scheduled to be closed ASAP for overcoming of output buffer limits.

当 slave没挂但被阻塞住了,好比正在loading Master发过来的RDB, Master的指令不能马上发送给slave,就会放在output buffer中(见oll是命令数量,omem是大小),在配置文件中有以下配置:client-output-buffer-limit slave 256mb 64mb 60, 这是说负责发数据给slave的client,若是buffer超过256m或者连续60秒超过64m,就会被马上强行关闭!!! Traffic大的话必定要设大一点。不然就会出现一个很悲剧的循环,Master传输一个大的RDB给Slave,Slave努力的装载,但还没装载 完,Master对client的缓存满了,再来一次。

平时能够在master执行 redis-cli client list 找那个cmd=sync,flag=S的client,注意OMem的变化。