高可用有两个含意:一是数据尽量不失落,二是保障服务尽可能可用。AOF 和 RDB 数据长久化保障了数据尽量不失落, 那么多节点来保障服务尽可能提供服务。
个别在理论生产中, 服务不会部署成单节点,次要是有三个起因.
- 容易呈现 单点故障,导致服务不可用
- 单节点解决所有的申请,吞吐量无限
- 单节点 容量无限
为了实现高可用,通常的做法是,将数据库复制多个正本以部署在不同的服务器上,其中一台挂了也能够持续提供服务。Redis 实现高可用有三种部署模式:主从模式 , 哨兵模式 , 集群模式。
一、主从模式
既然一台服务宕机了会导致提供不可用,那是不是能够思考多台就能够解决了。Redis 提供了主从模式。通过主从复制,将数据冗余一份复制到其余 Redis 服务器。
Master 节点,负责读写操作,Slave 节点,只负责读操作。
1、主从复制原理
主从模式采纳了读写拆散,所有数据的写操作只会在 Master 库上进行,Master 库有了最新的数据后,会同步给 Slave 库,这样,主从库的数据就是统一的。
这里要思考是主从库同步是如何实现的?Master 库数据是一次性传给 Slave 库,还是分批同步的?失常运行中又怎么同步呢?要是主从库间的网络断连了,从新连贯后须要再次全量同步还是只需局部同步呢?
主从复制包含全量复制,增量复制两种。redis2.8 版本之后还反对局部同步。
(1) 全量同步
个别当 Slave 第一次启动连贯 Master,认为是第一次连贯,就采纳全量复制,全量复制流程如下:
实现下面几个步骤后就实现了 Salve 节点初始化的所有操作,Slave 服务器此时能够接管来自用户的读申请。
redis2.8 版本之后,曾经应用 psync 来代替 sync,因为 sync 命令十分耗费系统资源,而且不反对局部同步,psync 的效率更高,反对局部同步,无关局部同步上面细说。
(2) 增量同步
Redis 增量复制是指 Slave 初始化后开始失常工作时,Master 服务器产生的写操作同步到 Slave 服务器的过程。
增量复制的过程次要是 Master 服务器每执行一个写命令就会向从服务器发送雷同的写命令,从服务器接管并执行收到的写命令。
(3) 局部同步
在 redis 2.8 版本之前,并不反对局部同步,当主从服务器之间的连贯断掉之后,Master 服务器和 Slave 服务器之间必须进行全量数据同步,此时 Slave 服务器会清空所有数据,再次加载 Master 的 RDB 文件。
然而从 redis 2.8 开始,即便主从连贯中途断掉,也不肯定须要进行全量同步,它能够反对局部同步,来提高效率。
它的工作原理大抵是这样:
通过这个图咱们再来了解下 为什么 2.8 局部能够实现局部同步
- Slave 节点依据以后状态,发送
psync
命令给 Master 节点:
- 如果 Slave 节点从未执行过
replicaof
,则 Slave 节点发送psync ? -1
,向 Master 节点发送全量复制申请; - 如果 Slave 节点之前执行过
replicaof
则发送psync <runID> <offset>
, runID 是上次复制保留的 Master 节点 runID,offset 是上次复制截至时 Slave 节点保留的复制偏移量。
- Master 节点依据承受到的
psync
命令和以后服务器状态,决定执行全量复制还是局部复制:
- runID 与 Slave 节点发送的 runID 雷同,且 Slave 节点发送的
slave_repl_offset
之后的数据在repl_backlog_buffer
缓冲区中都存在,则回复CONTINUE
,示意将进行局部复制,Slave 节点期待 Master 节点发送其短少的数据即可; - runID 与 Slave 节点发送的 runID 不同,或者 Slave 节点发送的 slave_repl_offset 之后的数据已不在 Master 节点的
repl_backlog_buffer
缓冲区中 (在队列中被挤出了),则回复 Slave 节点FULLRESYNC <runid> <offset>
,示意要进行全量复制,其中 runID 示意 Master 节点以后的 runID,offset 示意 Master 节点以后的 offset,Slave 节点保留这两个值,以备应用。
一个 Slave 库如果和 Master 库断连工夫过长,造成它在 Master 库 repl_backlog_buffer
的 slave_repl_offset 地位上的数据曾经被笼罩掉了,此时 Slave 库和 Master 库间将进行全量复制。
2、主从模式的优缺点
长处
- 做到读写拆散,进步服务器性能。Salve 能够分载 Master 的读操作压力,当然写服务仍然必须由 Master 来实现;
- 当 Master 节点服务挂了,能够让 Slave 变成 Master 节点持续提供服务;
毛病
- 在主从模式中,一旦 Master 节点因为故障不能提供服务,须要人工将 Slave 节点晋升为 Master 节点,同时还要告诉利用方更新 Master 节点地址。显然,少数业务场景都不能承受这种故障解决形式;
- redis 的 Master 节点和 Slave 节点中的数据是一样的,升高的内存的可用性, 而且存储能力也无限。
- 主从复制写还都是在 Master 节点, 所以写的压力并没有缩小。
因而,主从复制其实并不能满足咱们高可用的要求。
二、哨兵模式
在主从模式中,一旦 Master 节点因为故障不能提供服务,须要人工将 Slave 节点晋升为 Master 节点。显然,少数业务场景都不能承受这种故障解决形式。Redis 从 2.8 开始正式提供了 Redis Sentinel(哨兵)架构来解决这个问题。
哨兵模式
,由一个或多个 Sentinel 实例组成的 Sentinel 零碎,它能够监督所有的 Master 节点和 Slave 节点,并在被监督的 Master 节点进入下线状态时, 主动将下线 Master 服务器属下的某个 Slave 节点降级为新的 Master 节点。然而呢,一个哨兵过程对 Redis 节点进行监控,就可能会呈现问题(单点问题),因而,能够应用多个哨兵来进行监控 Redis 节点,并且各个哨兵之间还会进行监控。
sentinel 是一种非凡的 redis 实例,它不存储数据,只对集群进行监控。
简略来说,哨兵模式就三个作用:
- 通过发送命令,期待 Redis 服务器返回监控其运行状态, 包含 Master 服务器和 Slave 服务器;
- 当哨兵监测到 Master 节点宕机,会主动将 Slaver 节点切换成 Master 节点,而后通过公布订阅模式告诉其余的 Slave 节点,批改配置文件,让它们切换主机;
- 如果是只有一个哨兵对过程对 Redis 服务器进行监控,也可能会呈现问题,为此,咱们能够应用多个哨兵进行监控。它们之间还会互相监控,从而达到高可用。
1、哨兵次要工作工作
哨兵次要有三个定时监控工作实现对各节点的发现和监控。
工作 1 :每个哨兵节点每 10 秒会向 Master 节点和 Slave 节点发送 info 命令获取最拓扑结构图,哨兵配置时只有配置对 Master 节点的监控即可,通过向 Master 节点发送 info,获取 Slave 节点的信息,并当有新的 Slave 节点退出时能够马上感知到
工作 2 ,每个哨兵节点每隔 2 秒会向 redis 数据节点的指定频道上发送该哨兵节点对于 Master 节点的判断以及以后哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来理解其它哨兵节点的信息及对 Master 节点的判断,其实就是通过音讯 publish 和 subscribe 来实现的;
工作 3 ,每隔 1 秒每个哨兵会向 Master 节点、Slave 节点及其余哨兵节点发送一次 ping 命令做一次心跳检测,这个也是哨兵用来判断节点是否失常的重要依据
2、哨兵发现服务下线
这里能够分为 哨兵主观下线 和 哨兵主观下线
哨兵主观下线
下面说过哨兵节点每隔 1 秒对 Master 节点和 Slave 节点、其它哨兵节点发送 ping 做心跳检测,当这些心跳检测时间超过 down-after-milliseconds 时,哨兵节点则认为该节点 谬误或下线,这叫主观下线;
当然这但不代表这个 master 真的不能用(有可能网络稳定导致该哨兵与服务连贯异样),所以主观下线是不牢靠的,可能存在误判。
哨兵主观下线
当主观下线的节点是 Mater 节点时,此时该哨兵 节点会通过指令sentinelis-masterdown-by-addr 寻求其它哨兵节点对 Master 节点的判断,当超过 quorum(法定人数)个数,此时哨兵节点则认为该 Master 节点的确有问题,这样就主观下线了,大部分哨兵节点都批准下线操作,也就说是主观下线
3、主动故障转移机制
如果哨兵主观下线某 Master, 那是不是接下来要选举新的 Master,这个工作只有一个哨兵实现即可,所以首先要做的是选举一个哨兵领导者。
1) 领导者选举
起因:只有一个 sentinel 节点实现故障转移所以须要选举。选举通过 sentinelis-master-down-by-addr 命令心愿成为领导者:
- 每个做主观下线的 Sentinel 节点向其余 Sentinel 节点发送命令,要求将它设置为领导者
- 收到命令的 Sentinel 节点如果没有批准通过其余 Sentinel 节点发送的命令,那么将批准该申请,否则回绝
- 如果该 Sentinel 节点发现自己的票数曾经超过 Sentinel 汇合半数且超过 quorum,则将成为领导者
- 如果此过程有多个 Sentinel 节点成为了领导者,那么将期待一段时间从新选举
2) 在从节点中抉择新的 Master 节点
sentinel 状态数据结构中保留了主服务的所有从服务信息,领头 sentinel 依照如下的规定从从服务列表中筛选出新的主服务
- 过滤掉主观下线的节点
- 抉择 slave-priority 最高的节点,如果由则返回没有就持续抉择
- 抉择出 复制偏移量 最大的系节点,因为复制偏移量越大则数据复制的越残缺,如果由就返回了,没有就持续
- 抉择 run_id 最小的节点
3) 更新主从状态
通过 slaveof no one 命令,让选出来的 Slave 节点成为 Master 节点;并通过 slaveof 命令让其余节点成为其 Slave 节点。
将已下线的 Master 节点设置成新的 Master 节点的 Slave 节点,当其回复失常时,复制新的 Master 节点,变成新的 Master 节点的 Slave 节点
4、脑裂导致数据失落
1) 什么是脑裂
脑裂 也就是说,某个 Master 所在机器忽然脱离了失常的网络,跟其余 slave 机器不能连贯,然而实际上 master 还运行着。此时哨兵可能就会认为 master 宕机了,而后开启选举,将其余 slave 切换成了 master。这个时候,集群里就会有 两个 Master,也就是所谓的脑裂。
此时尽管某个 slave 被切换成了 master,然而可能 client 还没来得及切换到新的 master,还持续向旧 master 写数据。因而旧 master 再次复原的时候,会被作为一个 slave 挂到新的 master 下来,本人的数据会清空,从新从新的 master 复制数据。而新的 master 并没有起初 client 写入的数据,因而,这部分数据也就失落了。
2) 如果解决脑裂问题
Redis 曾经提供了两个配置项来限度 Master 库的申请解决,别离是 min-slaves-to-write
和 min-slaves-max-lag
。(2.8 当前改为 min-replicas-to-write 和 min-replicas-max-lag)
min-slaves-to-write 1
min-slaves-max-lag 10
如上两个配置:要求至多有 1 个 slave,数据复制和同步的提早不能超过 10 秒,如果超过 1 个 slave,数据复制和同步的提早都超过了 10 秒钟,那么这个时候,master 就不会再接管任何申请了。
这样一配置的话,就算你的 Master 库是假故障,那它在假故障期间也无奈响应哨兵心跳,也不能和 Slave 库进行同步,天然也就无奈和 Slave 库进行 ACK 确认了。原 Master 库就会被限度接管客户端申请,客户端也就不能在原 Master 库中写入新数据了。
当然这个配置做不到让数据一点也不失落,而是让数据尽可能的少失落。
5、哨兵模式的优缺点
长处
- 哨兵模式是基于主从模式的,所有主从的长处,哨兵模式都具备。
- 主从能够主动切换,零碎更强壮,可用性更高。
毛病
- 具备主从模式的毛病,每台机器上的数据是一样的,内存的可用性较低。
- 还要多保护一套哨兵模式,实现起来也变的更加简单减少保护老本。
- Redis 较难反对在线扩容,在集群容量达到下限时在线扩容会变得很简单。
3、Cluster 集群模式
先说一个误区:Redis 的集群模式自身没有应用一致性 hash 算法,而是应用 slots 插槽。因为我查了很多材料都是这么说的,至于为什么应用 slots 插槽,我集体了解 slots 插槽多少个是固定的,这样更加不便数据迁徙。
哨兵模式基于主从模式,实现读写拆散,它还能够主动切换,零碎可用性更高。然而它每个节点存储的数据是一样的,节约内存。因而,在 Redis3.0 后 Cluster 集群应运而生,它实现了 Redis 的分布式存储。对数据进行分片,也就是说 每台 Redis 节点上存储不同的内容,来解决在线扩容的问题。
一个 Redis Cluster 由多个 Redis 节点形成,节点组内局部为主备两类节点,对应 master 和 slave 节点。两者数据准实时统一,通过异步化的主备复制机制来保障。
一个节点组有且只有一个 master 节点,同时能够有 0 到多个 slave 节点,在这个节点组中只有 master 节点对用户提供些服务,读服务能够由 master 或者 slave 提供。如上图中,蕴含三个 master 节点以及三个 master 对应的 slave 节点,个别一组集群至多要 6 个节点能力保障残缺的高可用。
其中三个 master 会调配不同的 slot(示意数据分片区间),当 master 呈现故障时,slave 会主动选举成为 master 顶替 Master 节点持续提供服务。
1、集群的一些特点
1) redis cluster 模式采纳了无核心节点的形式来实现,每个 Master 节点都会与其它 Master 节点放弃连贯。节点间通过 gossip 协定替换彼此的信息,同时每个 Master 节点又有一个或多个 Slave 节点;
2) 客户端连贯集群时,间接与 redis 集群的每个 Master 节点连贯,依据 hash 算法取模将 key 存储在不同的哈希槽上;
3) 在集群中采纳数据分片的形式,将 redis 集群分为 16384 个哈希槽。如下图所示,这些哈希槽别离存储于三个 Master 节点中:
- Master1 负责 0~5460 号哈希槽
- Master2 负责 5461~10922 号哈希槽
- Master3 负责 10922~16383 号哈希槽
4) 每个节点会保留一份数据分布表,节点会将本人的 slot 信息发送给其余节点,节点间不停的传递数据散布表;
5) 客户端连贯集群时,通过集群中某个节点地址进行连贯。客户端尝试向这个节点执行命令时,比方获取某个 key 值,如果 key 所在的 slot 刚好在该节点上,则可能间接执行胜利。如果 slot 不在该节点,则节点会返回 MOVED 谬误,同时把该 slot 对应的节点通知客户端,客户端能够去该节点执行命令。
2、Master 节点故障解决形式
redis 集群中 Master 节点故障解决形式与哨兵模式较为相像,当约定工夫内某节点无奈与集群中的另一个节点顺利完成 ping 音讯通信时,则将该节点标记为主观下线状态,同时将这个信息向整个集群播送。
如果一个节点收到某个节点失联的数量达到了集群的大多数时,那么将该节点标记为主观下线状态,并向集群播送下线节点的 fail 音讯。而后立刻对该故障节点进行主从切换。等到原来的 Master 节点复原后,会主动成为新 Master 节点的 Slave 节点。如果 Master 节点没有 Slave 节点,那么当它产生故障时,集群就将处于不可用状态。
3、扩容问题
在 cluster 中咱们如何动静上线某个节点呢。当集群中退出某个节点时,哈希槽又是如何来进行调配的?当集群中退出新节点时,会与集群中的某个节点进行握手,该节点会把集群内的其它节点信息通过 gossip 协定发送给新节点,新节点与这些节点实现握手后退出到集群中。
而后集群中的节点会各取一部分哈希槽调配给新节点,如下图:
- Master1 负责 1365-5460
- Master2 负责 6827-10922
- Master3 负责 12288-16383
- Master4 负责 0 -1364,5461-6826,10923-12287
当集群中要删除节点时,只须要将节点中的所有哈希槽挪动到其它节点,而后再移除空白(不蕴含任何哈希槽)的节点就能够了。
4、对于 gossip 协定
无关 gossip 协定这里须要独自解释下
在整个 redis cluster 架构中,如果呈现以下状况
- 新退出节点
- slot 迁徙
- 节点宕机
- slave 选举成为 master
咱们心愿这些变动可能让整个集群中的每个节点都可能尽快发现,流传到整个集群并且集群中所有节点达成统一,那么各个节点之间就须要互相连通并且携带相干状态数据进行流传,
依照失常的逻辑是采纳播送的形式想集群中的所有节点发送音讯,有点是集群中的数据同步较快,然而每条音讯都须要发送给所有节点,对 CPU 和带宽的耗费过大,所以这里采纳了 gossip 协定。
Gossip protocol 也叫 Epidemic Protocol(流行病协定
),别名很多比方:“谰言算法”、“疫情流传算法”等。
它的特点是,在节点数量无限的网络中,每个节点都会“随机”(不是真正随机,而是依据规定抉择通信节点)与局部节点通信,通过一番横七竖八的通信后,每个节点的状态在肯定工夫内会达成统一,如下图所示。
假如咱们提前设置如下规定:
1、Gossip 是周期性的散播音讯,把周期限定为 1 秒
2、被感化节点随机抉择 k 个邻接节点(fan-out)散播音讯,这里把 fan-out 设置为 3,每次最多往 3 个节点散播。
3、每次散播音讯都抉择尚未发送过的节点进行散播
4、收到音讯的节点不再往发送节点散播,比方 A -> B,那么 B 进行散播的时候,不再发给 A。
这里一共有 16 个节点,节点 1 为初始被感化节点,通过 Gossip 过程,最终所有节点都被感化:
gossip 协定蕴含多种音讯,包含 ping,pong,meet,fail 等等。
ping:每个节点都会频繁给其余节点发送 ping,其中蕴含本人的状态还有本人保护的集群元数据,相互通过 ping 替换元数据;
pong: 返回 ping 和 meet,蕴含本人的状态和其余信息,也能够用于信息播送和更新;
fail: 某个节点判断另一个节点 fail 之后,就发送 fail 给其余节点,告诉其余节点,指定的节点宕机了。
meet:某个节点发送 meet 给新退出的节点,让新节点退出集群中,而后新节点就会开始与其余节点进行通信,不须要发送造成网络的所需的所有 CLUSTER MEET 命令。发送 CLUSTER MEET 音讯以便每个节点可能达到其余每个节点只需通过一条已知的节点链就够了。因为在心跳包中会替换 gossip 信息,将会创立节点间缺失的链接。
5、gossip 的优缺点
长处
: gossip 协定的长处在于元数据的更新比拟扩散,不是集中在一个中央,更新申请会陆陆续续,打到所有节点下来更新有肯定的延时,升高了压力;去中心化、可扩大、容错、一致性收敛、简略。因为不能保障某个时刻所有节点都收到音讯,然而实践上最终所有节点都会收到音讯,因而它是一个最终一致性协定。
毛病
: 元数据更新有延时可能导致集群的一些操作会有一些滞后。音讯的提早,音讯冗余。
参考资料
[1] Redis 高可用篇:主从数据同步原理: https://zhuanlan.zhihu.com/p/…
[2] 如何实现 Redis 高可用的: https://zhuanlan.zhihu.com/p/…
申明: 公众号如需转载该篇文章, 发表文章的头部肯定要 告知是转至公众号: 后端元宇宙。同时也能够问自己要 markdown 原稿和原图片。其它状况一律禁止转载!
关注关注号:后端元宇宙。继续输入优质好文。