复制
通过向一个redis实例发送slaveof ip port命令,可以使得该实例去复制另一个实例。我们称这个被复制的实例为master,而执行复制操作的实例为slave。通过复制,可以使master及其slave具备相同的数据库状态。
复制包含两部分操作:
- 同步sync 同步,使得slave具备master当前的数据库状态
- 命令传播 master在不断收到client的命令,数据库状态也在不断变化,通过命令传播,使得slave的状态动态的与master保持一致。
同步sync的两个阶段:
- slave向master发送sync的命令;
- master收到sync后,开始执行bgsave的命令来生成一个RDB快照,同时将期间收到的来自client的写命令记录下来;
- master生成RDB结束后,会将RDB发送给slave。slave会加载这个RDB文件,达到master开始生成RDB时刻的数据库状态;
- 之后,master将缓存里的写命令发送给slave,slave执行这写命令,使得slave与master数据库状态一致。
命令传播
master会不断的将client发送过来的写命令传播给slave,slave会执行这写命令,从而保证slave与master的数据库状态动态的保持一致。
假如slave与master断了怎么办?
旧的redis版本会重复整个sync的过程,使得slave再次与master一致,之后执行命令传播即可。但是这样效率太低。
新版本redis的处理方案
首先看三个概念
- 复制偏移量 master和slave均保存了一个复制偏移量。当master向slave传播了一个写命令时,master的复制偏移量会增加;当slave执行了这个写命令时,slave的偏移量也会增加。因此master和slave数据库状态一致等价于两者的复制偏移量相等。
- 复制积压缓冲区 master维护了一个FIFO的固定大小的队列。每次向slave传播写命令时,会将这个命令对应的写入这个队列。同时,还记录了这些命令对应的偏移量。
slave与master重连后的执行操作:
- slave向master发送psync的命令,表示要对数据重新进行同步,同时将自己的偏移量发送给master
- master收到这个偏移量后,如果积压缓冲区队列中包含这个偏移量对应的写命令,则将该写命令及其之后的写命令发送给slave,slave会执行这写命令,从而是的数据库状态恢复一致。我们称为部分重同步。
- 如果不包含,则执行完全重同步。即之前sync的整个过程。
note
- 积压缓冲区默认大小1M,大小视情况而定。
Sentinel
Sentinel(哨兵)是redis提供的高可用的方案。
Sentinel系统由一个或者多个Sentinel实例组成。他们可以监视一个或者多个master以及他们全部的slave。当有master挂掉后,sentinel系统可以自动执行故障转移操作,从而使得服务高可用。
自动故障转移
- sentinel发现一个master下线后,会从该master的slave节点中选出一个作为新的master节点;
- sentinel通知其他的slave节点复制新的master节点;
- sentinel会继续监视原先已下线的master,一旦它恢复,sentinel会将它变成新的master的slave。
那么,sentinel是如何判断master节点是否下线的呢?
- 主观下线 每个sentinel实例都会周期性的向全部的master节点发送ping消息,master也会返回pong。一旦一段时间内,sentinel没有收到有效的pong响应,就会认为这个master主观下线。
- 客观下线 一个sentinel实例判定某个master为主观下线之后,会寻求其他sentinel实例对这个master的看法,如果认为这个master主观下线的sentinel实例数达到一定的数量,sentinel系统就会判定这个master为客观下线状态。并对该master执行自动故障转移。
集群
Redis集群是redis提供的分布式数据库方案。数据分布在不同的集群master上,同时提供复制,故障转移等功能。
redis集群的数据分布是通过槽slot来分配的。整个redis集群一共有16384个槽,集群中的全部master节点分割全部的16384个槽。每个key通过 CRC16(key) & 16383 的方式来计算该key应该分配到哪个槽上,从而决定应该分布到那个节点上。
当client向redis集群的某个节点发送一个命令时,比如get name.收到命令的node会看name对应的槽是否是本节点来处理,如果是,直接返回结果;如果不是,会想client响应一个moved,同时把能够处理该key的节点响应给该client。之后client再次将get name这个命令发送给能够处理该key的节点。
除此之外,redis集群模式也支持在线的重新分片。即将某个节点处理的slot转交给另一个节点来处理。