Redis高可用(主从、哨兵、集群)

目录

主从复制

全量复制

增量复制

无硬盘复制

sentinel(哨兵)

什么是哨兵

sentinel之间的相互感知

master的故障发现

Redis-Cluster(集群)

拓扑结构

Redis的数据分区

HashTags

重定向客户端

分片迁移


主从复制

复制的作用是把redis的数据库复制多个副本部署在不同的服务器上,如果其中一台服务器出现故障,也能快速迁移到其他服务器上提供服务。 复制功能可以实现当一台redis服务器的数据更新后,自动将新的数据同步到其他服务器上。

主从复制就是我们常见的master/slave模式, 主数据库可以进行读写操作,当写操作导致数据发生变化时会自动将数据同步给从数据库。而一般情况下,从数据库是只读的,并接收主数据库同步过来的数据。 一个主数据库可以有多个从数据库。


全量复制

Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。

完成上面几个步骤后就完成了slave服务器数据初始化的所有操作,slave服务器此时可以接收来自用户的读请求。 

master/slave 复制策略是采用乐观复制,也就是说可以容忍在一定时间内master/slave数据的内容是不同的,但是两者的数据会最终同步。具体来说,redis的主从同步过程本身是异步的,意味着master执行完客户端请求的命令后会立即返回结果给客户端,然后异步的方式把命令同步给slave。这一特征保证启用master/slave后 master的性能不会受到影响。

但是另一方面,如果在这个数据不一致的窗口期间,master/slave因为网络问题断开连接,而这个时候,master是无法得知某个命令最终同步给了多少个slave数据库。不过redis提供了一个配置项来限制只有数据至少同步给多少个slave的时候,master才是可写的。

min-slaves-to-write 3 表示只有当3个或以上的slave连接到master,master才是可写的。

min-slaves-max-lag 10表示允许slave最长失去连接的时间,若10秒还没收到slave的响应,则master认为该slave已断开。


增量复制

从redis 2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份。

master node会在内存中创建一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制但是如果没有找到对应的offset,那么就会执行一次全量同步。


无硬盘复制

前面我们说过,Redis复制的工作原理基于RDB方式的持久化实现的,也就是master在后台保存RDB快照,slave接收到rdb文件并载入,但是这种方式会存在一些问题。

1. 当master禁用RDB时,如果执行了复制初始化操作,Redis依然会生成RDB快照,当master下次启动时执行该RDB文件的恢复,但是因为复制发生的时间点不确定,所以恢复的数据可能是任何时间点的。就会造成数据出现问题。

2. 当硬盘性能比较慢的情况下(网络硬盘),那初始化复制过程会对性能产生影响因此2.8.18以后的版本,Redis引入了无硬盘复制选项,可以不需要通过RDB文件去同步,直接发送数据。

通过以下配置来开启该功能:repl-diskless-sync yes

master在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了。

sentinel(哨兵)

在master/slave模式里,一个典型的一主多从的系统中,slave在整个体系中起到了数据冗余备份和读写分离的作用。当master遇到异常终端后,需要从slave中选举一个新的master继续对外提供服务,比如在zk中通过leader选举、kafka中可以基于zk的节点实现master选举。所以在redis中也需要一种机制去实现master的决策,redis并没有提供自动master选举功能,而是需要借助一个哨兵来进行监控。


什么是哨兵

哨兵是一个独立的进程,哨兵的作用就是监控Redis系统的运行状况。

它的功能包括两个:

  1. 监控master和slave是否正常运行。
  2. master出现故障时自动将slave数据库升级为master。

在解决master选举问题同时,又引出了一个单点问题,也就是哨兵的可用性如何解决?在一个一主多从的Redis系统中,可以使用多个哨兵进行监控任务以保证系统足够稳定。此时哨兵不仅会监控master和slave,同时还会互相监控,这种方式称为哨兵集群,哨兵集群需要解决故障发现、和master决策的协商机制问题。


sentinel之间的相互感知

sentinel节点之间会因为共同监视同一个master从而产生了关联,若一个新加入的sentinel节点想要和其他监视相同master节点的sentinel相互感知,则要满足以下几点:

  1. 需要相互感知的sentinel都向他们共同监视的master节点订阅channel:sentinel:hello
  2. 新加入的sentinel节点向这个channel发布一条消息,包含自己本身的信息,这样订阅了这个channel的sentinel就可以发现这个新的sentinel。
  3. 新加入得sentinel和其他sentinel节点建立长连接master的。

master的故障发现

sentinel节点会定期向master节点发送心跳包来判断存活状态,一旦master节点没有正确响应,sentinel会把master设置为“主观不可用状态”,然后它会把“主观不可用”发送给其他所有的sentinel节点去确认,当确认的sentinel节点数大于>quorum(规定数量)时,则会认为master是“客观不可用”,接着就开始进入选举新的master流程。

在选举新的master之前,需要从sentinel集群中选择一个leader来做决策,这里用到了一致性算法Raft算法,它和Paxos算法类似,都是分布式一致性算法,基于投票机制,只要保证过半数节点通过提议即可。

动画演示地址:http://thesecretlivesofdata.com/raft/


Redis-Cluster(集群)

即使是使用哨兵,此时的Redis集群的每个数据库依然存有集群中的所有数据,从而导致集群的总数据存储量受限于可用存储内存最小的节点,形成了木桶效应。而因为Redis是基于内存存储的,所以这一个问题在redis中就显得尤为突出了。

在redis3.0之前,我们是通过在客户端去做的分片,通过hash环的方式对key进行分片存储。分片虽然能够解决各个节点的存储压力,但是导致维护成本高、增加、移除节点比较繁琐。因此在redis3.0以后的版本最大的一个好处就是支持集群功能,集群的特点在于拥有和单机实例一样的性能,同时在网络分区以后能够提供一定的可访问性以及对主数据库故障恢复的支持。

哨兵和集群是两个独立的功能,当不需要对数据分片时使用哨兵就够了,如果要进行水平扩容,集群是一个较好的方式。


拓扑结构

一个Redis Cluster由多个Redis节点构成。不同节点组服务的数据没有交集,也就是每个一节点组对应数据sharding的一个分片。节点组内部分为主备两类节点,对应master和slave节点。两者数据准实时一致,通过异步化的主备复制机制来保证。一个节点组有且只有一个master节点,同时可以有0到多个slave节点,在这个节点组中只有master节点对用户提供写服务,读服务可以由master或者slave提供,当master挂掉,会从slave中选出一个成为新的master。


Redis的数据分区

分布式数据库首要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整个数据的一个子集, Redis Cluster采用哈希分区规则,采用虚拟槽分区。

虚拟槽分区巧妙地使用了哈希空间,使用分散度良好的哈希函数把所有的数据映射到一个固定范围内的整数集合,整数定义为槽(slot)。比如Redis Cluster槽的范围是0 ~ 16383。槽是集群内数据管理和迁移的基本单位。采用大范围的槽的主要目的是为了方便数据的拆分和集群的扩展,每个节点负责一定数量的槽。计算公式:slot = CRC16(key)%16383。每一个节点负责维护一部分槽以及槽所映射的键值数据。


HashTags

有些时候我们希望某一部分key可以落到同一个数据分片上。

例如对于用户信息:user:user1:id、user:user1:name;

通过hashtag的方式可以表示为:user:{user1}:id、user:{user1}.name;

当一个key包含 {} 的时候,就不对整个key做hash,而仅对 {} 包括的字符串做hash。


重定向客户端

Redis Cluster并不会代理查询,那么如果客户端访问了一个key并不存在的节点,这个节点是怎么处理的呢?比如我想获取key为msg的值,msg计算出来的槽编号为254,当前节点正好不负责编号为254的槽,那么就会返回客户端下面信息:-MOVED 254 127.0.0.1:6381。表示客户端想要的254槽由运行在IP为127.0.0.1、端口为6381的Master实例服务。如果根据key计算得出的槽恰好由当前节点负责,则当期节点会立即返回结果。


分片迁移

在一个稳定的Redis cluster下,每一个slot对应的节点是确定的,但是在某些情况下,节点和分片对应的关系会发生变更。也就是说当动态添加或减少node节点时,需要将16384个槽做个再分配,槽中的键值也要迁移。

一. 新增一个主节点

新增一个节点D,redis cluster的这种做法是从各个节点的前面各拿取一部分slot到D上。大致就会变成这样:

节点A覆盖1365-5460

节点B覆盖6827-10922

节点C覆盖12288-16383

节点D覆盖0-1364,5461-6826,10923-12287

二. 删除一个主节点

先将节点的数据移动到其他节点上,然后才能执行删除。


槽迁移的过程

槽迁移的过程中有一个不稳定状态,这个不稳定状态会有一些规则,这些规则定义客户端的行为,从而使得RedisCluster不必宕机的情况下可以执行槽的迁移。

下面这张图描述了我们迁移编号为1、2、3的槽的过程中,他们在MasterA节点和MasterB节点中的状态。

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@从入门到入土

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值