一、主从复制
1、命令
PSYNC:该命令具有完整重同步和部分重同步。
完成重同步:通过主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区那里面的写命令来进行同步。
部分重同步:当从服务器在断线重连主服务器之后,则主服务器将主从服务器断开期间执行的写命令发送给从服务器。从服务器接收并执行这些写命令。
部分重同步实现:
1)主服务器的复制偏移量和从服务器的复制偏移量
1、主服务器每次向从服务器传播N个字节的数据时,就将自己的复制偏移量的值加上N
2、从服务器每次接收到主服务器传播来的N个字节的数据时,就将自己的复制偏移量的值加上N
通过对比主从服务器的偏移量可以确定主从服务器状态是否一致。
2)主服务器的复制积压缓冲区
主服务器维护的一个固定长度,先进先出队列,默认大小1MB。
当主服务器进行命令传播时,它不仅将写命令发送给所有从服务器,还会讲写命令入队到复制积压缓冲区。
当从服务器重连时,从服务器通过PSYNV命令将自己的复制偏移量offset发送给主服务器,主服务器有以下抉择:
1、如果offset偏移量之后的数据,仍然存在于复制缓冲区,那么主服务器将对从服务器执行部分重同步操作
2、相反,如果不存在,则执行完整的重同步操作。
3)服务器的运行ID(run ID)
1、每个redis服务器,无论主服务器还是从服务器,都会有自己的运行ID
2、运行ID在服务器启动时自动生成,由40个随机的十六进制字符组成。
主服务器根据从服务器发送过来的运行ID,决定执行部分重同步还是完整重同步。
2、复制实现
1)从服务器设置主服务器的地址和端口:SLAVEOF
2)建立套接字连接:从服务器根据IP和端口,创建连接。
3)发送ping命令:检查读写状态;检查主服务器能否正常处理命令请求
4)身份验证
5)发送端口信息:REPLCONF listening-port
6)同步:从服务器向主服务器发送PSYNC命令,执行同步操作。
7)命令传播
二、哨兵(Sentinel)
1、概念
有一个或多个Sentinel实例组成的Sentinel系统,可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在主服务器
进入下线状态时,自动通过投票方法,将主服务器属下的某个从服务器升级为新的主服务器。
2、启动并初始化Sentinel
$ redis-sentinel /path/to/you/sentinel.conf 或者通过命令 $ redis-server /path/to/your/sentinel.conf --sentinel
1)初始化服务器
Sentinel本质是一个运行在特殊模式下的Redis服务器,但是初始化是与普通的服务器不同,不载入RDB文件或者AOF文件。
2)将普通redis服务器使用的代码替换成Sentinel专用代码
3)初始化Sentinel状态
初始化Sentinel状态中的masters字典,里面记录了所有呗Sentinel监视的主服务器相关信息。
4)根据给定的配置文件,初始化Sentinel的监视主服务器列表
初始化上面提到的字典。
5)创建连向主服务器的网络连接
Sentinel会创建两个连向主服务器的异步网络连接
1、命令连接:向主服务器发送命令,并接收命令回复
2、订阅连接,订阅主服务器的_sentinel_:hello频道
当Sentinel发现有新的从服务器出现时,会创建连接到从服务器的命令连接和订阅连接。
3、选举领头Sentinel
当一个主服务器被判断客观下线时,监视这个下线主服务器的各个Sentinel会进行协商,选举出一个领头Sentinel,并由领头Sentinel对下线主服务器
执行故障转移操作:
1、设置规则:
1)所有sentinel(源)都向另一个sentinel(目的)发送命令,要求目的将自己设置为局部sentinel(源)
2)先到先得,最先向目标Sentinel发送设置要求的源Sentinel将成为Sentinel的局部Sentinel,
3)如果某个sentine被半数以上的sentinel设置为局部领头,则这个sentinel成为领头sentinel。
4)如果规定时间没有选举出,则再次选举。
4、故障转移
领头sentinel在已下线主服务器属下的所有从服务器中,挑选出一个状态良好、数据完整的从服务器,将其转换为主服务器。
所有从服务器都保存在一个列表里面,将通过以下规则进行过滤:
1)删除下线或者断线的从服务器
2)删除类表中5秒内没有回复过领头sentinel的INFO命令的从服务器
3)删除与已下线主服务器连接断开超多规定时间的从服务器。
4)选举优先级搞的从服务器
5)如果有多个相同优先级的从服务器,则按照从服务器的复制偏移量,挑选出偏移量最大的从服务器
6)如果还有多个,则挑选运行ID最小的从服务器。
三、集群
1、节点
1)一个redis集群通常由多个节点(每个节点就是一个redis服务器)组成,在刚开始的时候,每个节点都是相互独立的,
都处于只包含自己的集群里,需要将各个独立的节点连接起来,才能构成一个包含多个节点的集群
2)连接命令:CLUSTER MEET
3)启动节点:根据配置:cluster-enabled是否为yes来决定是否开启服务器的集群模式。
4)握手过程(A->B):
1、节点A为B创建一个clusterNode结构,并将该结构添加到自己的clusterStater.nodes字典里
2、A根据CLUSTER MEET命令给定的IP和port,向B发送一条MEET消息
3、B接收到A的MEET消息,为A创建一个clusterNode结构,并添加到自己的字典里
4、B向A发送一条PONG消息
5、A接收到B的PONG消息,发送一条PING消息。
至此握手完成,之后A会将节点B的信息通过Gossip协议传播给集群的其他节点,让其他节点和B进行握手,
经过一段时间之后,节点B会被集群中的所有节点认识。
2、槽指派
redis集群通过分片的方式来保存数据库中的键值对:集群的整个数据库被分为16384个槽(slot),数据库中的每个键都属于这16384个slot的其中一个,集群中的每个
节点可以处理0个或最多16384个slot。
1)指派命令:CLUSTER ADDSLOTS
2)slots属性:每个节点都包含一个slots属性,其是一个二进制数组,包含16384个字节,以0为起始索引,16383为终止索引。节点根据索引上的二进制位的值
来判断是否负责处理槽i,如果二进制位上的值为1,则负责处理该槽,为0则不负责处理该槽。
节点之间通过传播节点的槽指派信息,获知其他节点的槽指派信息,并将其保存在为其创建的slusterNode节点里。
3、集群中执行命令:
1)执行过程:客户端向某个节点发起命令请求,如果该节点保存,则回复;否则返回一个MOVED错误,并直营客户端转向正确节点,并再次发送之前想要执行的命令。
MOVED错误格式:MOVED
2)计算键属于哪个槽:
def slot_number(key) return CRC16(key) & 16383
先计算CRC-16校验和,之后&16383,确定键key的槽号。
3)节点数据库的实现
集群节点数据库保存键值对以及键值对过期时间的方式,和单机服务器相同。区别在于,节点只能使用0号数据库,单机数据库没有限制。
4、重新分片
redis集群重新发片操作由redis的集群管理软件redis-trib负责执行,redis-trib通过向源节点和目标节点发送命令来进行重新分片
1)命令:
1、redis-trib对目标节点发送 CLUSTER SETLOT
2、redis-trib对源节点发送 CLUSTER SETLOT
3、redis-trib对源节点发送 CLUSTER GETKEYSINSLOT
4、对于步骤三获得的每个键名,redis-trib对源节点发送MIGRATE
5、重复步骤三和步骤四,将所有属于槽的键值对进行迁移。
6、向任意一个节点发送CLUSTER SETSLOT
5、复制和故障转移
redis集群中的节点分为主节点和从节点,主节点负责处理槽,从节点用于复制某个主节点,并在主节点下线时,代替主节点继续处理命令。
1)设置从节点命令:CLUSTER REPLICATE
2)故障检测:集群中的每个节点都会定期向集群中的其他节点发送ping消息,如果目标没有在规定时间返回pong消息,则被标记为疑似下线。如果半数以上节点将该节点标记为疑似下线,则目标主节点表标记为已下线。
例如:有四个主节点7000,7001,7002,7003。如果7001收到主节点7002、主节点7003发送的主节点7000疑似下线消息,并且自己也认为该节点下线。
那么主节点7001将在集群中广播一条主节点7000的FAILL消息,所有收到的消息的节点将主节点7000标记为已下线。
3、故障转移
1)复制下线主节点的所有从节点里面,会有一个从节点被选中
2)被选中的节点执行SLAVEOF no one命令,成为新的主节点
3)新的主节点会侧小所有对已下线主节点的槽指派,并将槽全部派给自己
4)新的主节点广播一条pong消息,告知自己接管工作。
5)新主节点开始工作
4、选举新的主节点
1)集群里每个负责处理槽的主节点都有一次投票机会
2)从节点发现自己正在复制的主节点进入下线状态,会向集群中广播,要求接收到消息的并且具有投票权的主节点向这个从节点投票
3)具有投票权的主节点向第一个发送给他的消息的从节点投票
4)当一个从节点收到N/2+1张支持票时,则该从节点会当选为新的主节点。
5)如果一个配置纪元(一轮投票)里没有收集到足够多的选票,则再次进行。
选举新节点的方法和选举领头sentinel的方法相似,两者都给予RAFT算法的领头选举方法实现。