redis的一些机制保证了它的单点可用性但是随着业务增长这个单点的处理能力总有上限而且把数据集中到一台服务器的风险也太过巨大,所以引入了集群
单点可用性:
redis的持久化机制
redis的过期删除和内存淘汰
redis集群是采用主从复制的原理,主服务器和外界直接交互,它在执行完写操作后会将指令转发给从服务器,从服务器执行完就可以保证数据的最终一致性了。
主从复制过程
我们可以使用 replicaof(Redis 5.0 之前使用 slaveof)命令形成主服务器和从服务器的关系。
比如,现在有服务器 A 和 服务器 B,我们在服务器 B 上执行下面这条命令:
# 服务器 B 执行这条命令
replicaof <服务器 A 的 IP 地址> <服务器 A 的 Redis 端口号>
首次同步
- 从服务器执行
replicaof
指令后会给主服务器发送一个psync
命令,里面包含主服务器的runID
和复制进度offset
(这俩参数初始一个是?一个是-1)。
runID,每个 Redis 服务器在启动时都会自动生产一个随机的 ID 来唯一标识自己。
offset,表示复制的进度。 - 主服务器收到
psync
命令后,会用FULLRESYNC
作为响应命令返回给对方,带上主服务器的runID
和主服务器目前的复制进度offset
。
FULLRESYNC的意图是进行全量复制,从服务器接收到消息后会存下这俩参数。 - 主服务器执行 bgsave 命令来生成 RDB 文件,然后把文件发送给从服务器。
这个过程和重写机制一样是异步进行的,拷贝期间新的写指令会缓存到replication buffer 缓冲区
- 从服务器接收到RDB文件后,将原来的数据清空然后导入RDB,之后给主服务器发送一个消息告诉他导入完成。
- 主服务器将
replication buffer 缓冲区
中的指令发送给从服务器,完成【主服务器将开始拷贝RDB】到【主服务器接收到从服务器完成数据拷贝】这段时间内的数据同步。 - 从服务器进行数据同步,至此首次连接完成。
命令传播
在完成首次同步
后双方保持长连接
状态,redis中这种模式称为基于长连接的命令传播
树状结构
如果采用扁平化结构的话对主服务器的性能影响太大了,毕竟与业务直接交互的是主服务器,从服务器倒是挺清闲,所以使用树状结构可以很好的分摊主服务器的负担,从服务器也可以作为下方从服务器的主服务器。
实现起来只需要执行下面这条指令:
replicaof <目标服务器的IP> 6379
此时如果目标服务器本身也是「从服务器」,那么该目标服务器就会成为「经理」的角色,不仅可以接受主服务器同步的数据,也会把数据同步给自己旗下的从服务器,从而减轻主服务器的负担。
增量复制
redis主/从服务器之间通信是借助RESP
协议进行传输的,尽管传输层借助TCP协议实现了相对可靠的传输,但是一旦发生网络异常,比如断网的话数据的同步问题又出现了,redis 2.8之前采用RDS重传机制,即重新走一次首次同步
,显然负担很大,redis2.8之后引入了增量复制
技术。
增量复制流程
- 主服务器给从服务器发送指令的时候会在
repl_backlog_buffer
环形缓冲区缓存指令,同时记录当前位置 - 从服务器收到指令的时候也会记录一个偏移量
- 假设现在从服务器断网后恢复会给主服务器发送一个
psync
消息告诉他自己的偏移量 - 主服务器比较两个偏移量如果丢失数据太多的话就进行
全局拷贝
否则将缓存的指令发过去就行了 - 所以缓冲区的大小应该设的大些
丢失数据量的话很简单偏移量维持一个递增的数,对缓冲区大小取余就可以找到数据存储的位置了,比较这个偏移量的差值是否大于缓冲区的承载大小就可以判断是否应该进行全局拷贝了
哨兵机制
前面叙述的都是在主服务器正常的情况下,主服务器不正常的话岂不直接宕机了?哨兵机制应运而生,哨兵会检测主服务器的状态,主服务器不正常的话选一个从服务器黄袍加身,下面看一下实现机制。
哨兵会定时ping
一下主服务器,ping是基于ICMP协议的它的作用正是查看网络设备的网络情况是否异常,如果在规定的时间内没有回应便会将主服务器标记为主观下线。
哨兵集群分布在不同的设备上,作用是尽量排除哨兵设备出问题的可能性,当一个哨兵将主服务器标记为主观下线后就会通知其它哨兵,其它哨兵会对这个主服务器进行ping操作,ping不通的哨兵的比例达到一个阈值的话就认为这个主服务器客观下线
。
当一个哨兵判断服务器已经客观下线的时候其它哨兵会按照自己ping的结果进行投票,当投票数达到一定的比例则认为该服务器宕机了,由选出的哨兵头子选一个新的主服务器。
主从故障转移
主服务器宕机后处理的哨兵会进行节点选择,从从服务器中选择一个网络状态好的同时数据保存相对完整的节点作为主节点,那么怎么判断从节点之前的网络连接状态不好呢?
Redis 有个叫 down-after-milliseconds * 10 配置项,其 down-after-milliseconds 是主从节点断连的最大连接超时时间。如果在 down-after-milliseconds 毫秒内,主从节点都没有通过网络联系上,我们就可以认为主从节点断连了。如果发生断连的次数超过了 10 次,就说明这个从节点的网络状况不好,不适合作为新主节点。
接下来要对所有从节点进行三轮考察:优先级、复制进度、ID 号。在进行每一轮考察的时候,哪个从节点优先胜出,就选择其作为新主节点。
- 第一轮考察:哨兵首先会根据从节点的优先级来进行排序,优先级越小排名越靠前,(Redis 有个叫 slave-priority 配置项,可以给从节点设置优先级)
- 第二轮考察:如果优先级相同,则查看复制的下标,哪个从「主节点」接收的复制数据多,哪个就靠前。(
slave_repl_offset
最大 ) - 第三轮考察:如果优先级和下标都相同,就选择从节点 ID 较小的那个。(都一样就随便选一个)
转换过程:
- 哨兵给选择的节点发送
SLAVEOF no one
让他升级为主节点,同时加大发送INFO
命令的频率,直到返回结果从slave 变为 master表示它完成了转变 - 哨兵给其它从节点发送
SLAVEOF
命令告诉它们新的老大是谁 - 通知客户端主节点改变,这个是通过 Redis 的发布者/订阅者机制来实现,主从切换完成后,哨兵就会向 +switch-master 频道发布新主节点的 IP 地址和端口的消息,这个时候客户端就可以收到这条信息,然后用这里面的新主节点的 IP 地址和端口进行通信了。
哨兵集群的建立:哨兵节点之间是通过 Redis 的发布者/订阅者机制来相互发现的,在主从集群中,主节点上有一个名为__sentinel__:hello的频道,不同哨兵就是通过它来相互发现,实现互相通信的。
主节点知道所有「从节点」的信息,所以哨兵会每 10 秒一次的频率向主节点发送 INFO 命令来获取所有「从节点」的信息。