1 主从复制
现在从服务器上绑定主服务器。
replicaof <服务器 A 的 IP 地址> <服务器 A 的 Redis 端口号>
开启第一次同步
- 第一阶段:建立链接、协商同步
- 从 -> 主服务器发送psync命令。第一次同步时从节点不知道主节点 runid,所以传
?,offset-1,表示请求全量同步。 - 主 -> 从服务器返回 FULLRESYNC(全量复制) 。两个参数:主服务器的 runlD 和 offset 。
- 从 -> 主服务器发送psync命令。第一次同步时从节点不知道主节点 runid,所以传
- 第二阶段:主服务器同步数据给从服务器
- 主 执行bgsave命令生成RDB文件 -> 从服务器收到RDB文件后,先清空再载入RDB。
- 为了主从一致性,主服务器在下面这三个时间间隙中将收到的写操作命令,写入到replication buffer 缓冲区里:
- 主服务器生成RDB文件期间;
- 主服务器发送RDB文件给从服务器期间;
- 「从服务器」加载RDB文件期间。
- 第三阶段:主服务器发送新写操作命令给从服务器
- 从服务器完成RDB的载入后,会回复一个确认消息给主服务器。
- 接着,主服务器将 replication buffer 缓冲区里的写操作发送给从服务器,完成一致性要求。
至此,主从服务器的第一次同步的工作就完成了。主从服务器在完成第一次同步后,双方之间就会维护一个 TCP 连接。后续主服务器可以通过这个连接继续将写操作命令传播给从服务器。
从服务器经理
如果绑的是从服务器,会把目标服务器当作"经理"。实质就是为了防止主服务器同步压力过大,让从服务器也可以传递同步行为。变成了 主 -> 从 -> 从 演变成了树状结构,那由经理同步的自然是要比经理本身慢一拍的。
增量复制
当主从之间网络波动,重连后怎么同步呢?全量复制开销过大。所以采用增量复制,将新增加的同步。肯定需要一个队列来维护增量,并且像这种可能超量写入的肯定采用环形队列,mySQL的redo log也有类似循环设计。还需要一个指针,并且主服务器记自己写的地方,而从服务器记自己读的地方。
在Redis中分别为repl_backlog_buffer和replication offset
那么,判断复制那些就很简单了,如果从服务器能在缓存区找到自己的指针记录的写操作,同步即可。找不到说明已经被覆盖,时间过于久远,需要全量同步。理所当然,可以通过优化缓存区来减少全量同步
2 哨兵机制
主从复制为了防止主宕机,但总不能24小时看着把,哨兵就是自动化主从节点切换的机制。
判断主服务器挂了
- 主观下线:没连上,但不知道是挂了还是网络波动
- 客观下线:大家都连不上(投票),大抵是真挂了。所以哨兵是要集群部署的,一般3个。
由提出主观下线的哨兵发起投票,票数过半则通过视为客观下线(第一次投票),且由发起者请求成为Leader负责主从切换(第二次投票)。
这里存在并发问题,两个哨兵同时发现客观下线,同时竞争Leader,所以投票默认给自己一票,并且本轮投票周期内只投一次票。也就是先到先得。
一般这种选举制都采用奇数,这样易判定过半。当哨兵挂的过半会出现,第一次投票过了,第二次投票无法通过的情况。
主从故障转移
- 步骤一:选出新主节点
- 先筛选掉网络差的,down-after-milliseconds 10次
- 优先级排序选择,小在前
- 哪个复制内容多
- ID小的
- SLAVEOF no one 命令去提示选中的节点升级为主节点,每秒INFO监督,返回master说明升级成功
- 步骤二:将从节点指向新主节点
- SLAVEOF 命令去提示从节点链接新主节点
- 步骤三:通知客户的主节点已更换
- 发布者/订阅者机制来实现通知客户端。switch-master频道。(这个是哨兵自己的消息订阅)
- 步骤四:将旧主节点变为从节点
- SLAVEOF 发给原来的主节点(假如他上线了)让他也变成从节点。
哨兵集群
Redis的分布订阅机制,__sentinel__:hello频道实现哨兵集群的自动化发现与订阅。
获取从节点信息:主节点知道所有「从节点」的信息,哨兵会每10秒向主节点发送INFO命令来获取所有「从节点」的信息。
3 Cluster 集群
哨兵虽然解决了主从问题,但还是单体的,没有解决切片储存。cluster就是一个专门用于切片的集群架构。
分片过程
先用CRC16算法生成16bit的数,对16384取模。所有的key都分为这16384个槽中的一个。切片就是分配这个槽位。
MOVED重定向
客户端数据读写 -> 某Redis节点 ,如果该的槽不是在该节点上(发生扩容,主从切换),返回MOVED重定向错误,并携带目标节点的IP和port端口。
ASK 重定向
MOVED采用在迁移完成的情况,客户收到后会改路由表,下次找该槽点会去新节点。而ASK是迁移中,ASK提示客户端,这次你的key在其他节点,临时重定向,客户端不会改路由表。
Gossip协议
分享方式是周期性的随机选择一些节点,并把信息传递给这些节点。
- meet消息:通知新节点加入。
- ping消息:每秒会向集群中其他节点发送ping消息,带着已知的两个节点的地址、槽、状态信息、最后一次通信时间等
- pong消息:当接收到ping、meet消息时,作为响应消息回复。消息中同样带有自己已知的两个节点信息。
- fail消息:当节点判定集群内另一个节点下线时,会向"集体"广播一个fail消息,其他节点接收到fail消息之后把对应节点更新为下线状态。
故障节点切换
依旧是主客观下线。
- 如果主观下线会在ping里放一个pfail并有限广播。
- 如果发现pfail的数量到半数以上,由发现者全体广播fail,提示所有节点该节点客观下线。
- 故障恢复:如果下线节点的是主节点,则需要在它的从节点中选一个替换它,流程如下:
- 资格检查
- 准备选举时间
- 发起选举
- 选举投票:只有持有槽的主节点才有票