Redis高可用-主从,哨兵,集群

主从复制

Master-Slave主从概念

同时运行多个redis服务端,其中一个做为主(master),其余的一个或多个做为从(slave),主从之间经过网络进行通信,slave经过复制master的数据来保持与master的数据同步,实现数据冗余;node

在Redis中,配置主从复制很是简单,Redis容许slave实例对master进行完整拷贝,在链接断开时,slave会自动从新链接至主实例,并尽量与master保持同步;redis

三个主要机制:

  • 当链接可用时,master将发送命令流到slave来使salve保持更新,如下操做将引起该操做,对msater数据的写入操做(包括删除更新),key过时;
  • master和slave节点都会进行超时检测,当链接不稳定时,slave将尽快从新链接并进行部分从新同步,即不须要彻底从新同步;
  • 若没法进行部分从新同步,则slave将发起彻底从新同步,master会将最新的数据快照发送给slave,后续的操做仍然是发送命令流;

其余特性:

主从之间,采用异步复制,复制过程当中依然能够正常响应客户端操做,支持一主多从,且从节点还能够相似的级联其余从节点,如图所示:算法

image-20200526184118562

主从复制的做用:

  • 使用主从复制,可以避免Redis的单点故障,实现数据防灾备份;
  • 可配置主节点执行写入,多个从节点分担查询,实现读写分离得到更好的性能

注意:使用主历来作读写分离时,意味主节点自身没有任何持久化数据;若是配置了哨兵,一旦节点重启,则将使用空数据进行同步,致使从节点覆盖全部持久化数据,这是很是危险的,墙裂建议在主节点和从节点上开启持久化,若是必定要关闭,则必须配置主节点禁止哨兵自动重启故障节点;具体故障模式:链接shell

配置:

#配置文件中按如下方式添加主节点的ip 和端口便可
replicaof 192.168.1.1 6379
#若主节点配置了受权密码则须要指定密码
masterauth 密码
#主节点经过如下方式设置受权密码
requirepass 密码
#客户端链接后须要先验证密码
auth 密码

#可经过如下指令查看当前链接的服务的主从信息
info replication

副本只读:

默认状况下副本是只读的,若须要能够经过配置replica-read-onlyno来使副本变为可写的,可是要强调的是副本写入的数据,仅写入到当前副本本地,不会同步至任何节点,包括当前副本的副本;当副本重启后这写数据将消失,即临时数据;安全

哨兵

哨兵(Sentinel) 是为redis提供了高可用性(high available/HA),使用Sentinel部署Redis时,可实如今无需人工干预的状况下,自动完成固定类型的故障修复;是Redis尽量处于正常工做状态;服务器

哨兵的主要功能:网络

  • 集群监控:监控redis各个节点是否正常工做;
  • 消息通知:当某个节点出现故障时,可经过API\通知系统管理员或是其余程序;
  • 故障转移(failover):当master没法没法正常工做时,哨兵能够启动故障转移过程,该过程会将某一个slave节点提高为master节点,并主动通知使用redis服务器的应用程序要使用新的地址;
  • ​ 配置中心:当客户端链接至哨兵时,可经过哨兵获取可用的Redis服务信息;

哨兵的分布式性质:异步

Sentinel自己是一个分布式系统,即会有多个Sentinel进程经过网络协同合做,具备如下优势:分布式

  • 当多个哨兵就某一master不可用这一事实达成共识,才会进行故障转移,下降了因网络波动形成误报的可能性;性能

  • 即便一些哨兵进程没法工做时,其余可用的哨兵仍然可以正常工做,提供了整个系统应对故障的能力;

    反过来,若是只有单独的一个哨兵进程其实是没有办法提供高可用的;

启动哨兵

哨兵的执行文件本质就是redis的服务端,只不过运行的模式不一样了,另外运行哨兵必须提供配置文件,不然将拒绝启动;

首先须要准备配置文件,在下载的源码中找到sentinel.conf 为了后续方便修改能够将其复制到bin目录下

指定master的地址和端口

sentinel monitor mymaster 127.0.0.1 6379 2

启动哨兵的命令有两种写法:

#方式1
redis-sentinel sentinel.conf
#方式1
redis-server sentinel.conf --sentinel

若启动哨兵成功能够在控制台中看到其输出的master节点信息;

image-20200527114018119

默认状况下哨兵监听在26379端口上,若开启了防火墙则须要开放该端口,不然哨兵没法正常工做;

完成故障切换的过程

故障切换的定义:

​ 当master不可用时将一个可用的slave提高称为master,使结点保持正常访问;

基于网络存在不稳定性这个特性,一些时候某个哨兵进程可能没法与master正常通信,可是这并不意味这master真的不可用了,哨兵没法就此认定master不可用这一事实,哨兵须要可以检测master是否客观的不可用了,并在master不可用成为客观事实后开始执行故障切换;

  1. 每一个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器 以及其余Sentinel(哨兵)进程发送一个 PING 命令
  2. 若是一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after- milliseconds 选项所指定的值,则这个实例会被 Sentinel(哨兵)进程标记为主观下线(sdown)
  3. 若是一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的全部Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态。
  4. 当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN)。
  5. 在通常状况下, 每一个Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的全部Master主服务器、Slave从服务器发送 INFO 命令。
  6. 当Master主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的 Master主服务器的全部 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改成每秒一次。
  7. 若没有足够数量的 Sentinel(哨兵)进程赞成 Master主服务器下线, Master主服务器的客观下线状态就会被移除。若 Master主服务器从新向 Sentinel(哨兵)进程发送 PING 命令返回有效回 复,Master主服务器的主观下线状态就会被移除。

故障切换涉及到的事件和参数:

  • sdown:主观下线,当某个哨兵与master之间在指定时间内没法正常通信后该哨兵将产生sdown事件

  • quorum(仲裁数):是一个整数,表示master从主观下线变为客观下线所须要的哨兵数量(但有quorum个哨兵与master通信失败则master进入主观下线)

  • odown:当sdown的事件的数量达到指定值(quorum)时,将产odown事件,表示master客观下线了;

  • majority(大多数):是一个整数,该值经过计算自动得出,计算公式为floor(哨兵总数量/2)+1 floor为下取整

当odown产生时,会选出一个哨兵准备进行故障切换,在切换前该哨兵还须要得到大多数(majority)哨兵的受权,受权成功则开始进行故障切换;

故障切换完成后,若先前宕机的节点(原来的master)恢复正常,则该节点会降为slave;

部署Sentinel的基本知识

  • 哨兵应与分布式的形式存在,若哨兵仅部署一个则实际上没有办法提升可用性,当仅有的哨兵进程遇到问题退出后,则没法完成故障恢复;

  • 一个健壮的部署至少须要三个Sentinel实例

  • 三个哨兵应该部署在相互的独立的计算机或虚拟机中;

  • Sentinel没法保证在执行故障转移期间的写入的数据是否可以保留下来;

部署案例:

下例图中名称的释义:
S:sentinel

M:master

R:replace(slave)

1.不要采用下例部署

image-20200528125919633

上述部署中若M1(包括S1,由于在同一个机器上)宕机,剩下的S2虽然能够认定M1主观下线,可是却没法获得大多数哨兵的受权并开始故障切换,由于此时majority为2;

2.简单且实用的部署

image-20200528130347251

上述部署由三个节点组成,每一个节点都运行着Redis进程和Sentinel进程,结构简单,安全性也有了提升;

当M1发生故障时,S2和S3能够就该故障达成一致,而且可以受权进行故障切换,从而使得客户端能够正常使用;但也存在丢失已写入数据的状况,由于redis内部使用异步复制,Master和Slave之间的数据在某个时间点可能不一致;

注意:

image-20200528130949015

因为网络存在分区性质,若客户端C1和M1处于同一分区,可是该分区与S1,S2所在的分区没法通信时,C1能够继续向M1写入数据,这写数据将丢失,由于当网络恢复时,M1可会被降为slave,而丢弃本身本来的数据;

使用下例配置可缓解该问题:

min-replicas-to-write 1
min-replicas-max-lag 5

上述配置表示,只要master没法写入数据到任何一个slave超过5秒,则master中止接受写入;

3.在客户端部署哨兵

若某些缘由致使没有足够的服务器节点用于部署哨兵,则能够将哨兵部署至客户端,以下所示

image-20200528132017887

若M1出现故障,则S1,S2,S3可顺利的进行故障切换;但要注意该部署可能出现案例2中的问题

固然你也能够在客户端和服务端同时部署哨兵;

配置示例:

#指出master地址和端口  以及仲裁数
sentinel monitor mymaster 127.0.0.1 6379 2
# 与master通信超时时间,达到超时时间则sdown+1
sentinel down-after-milliseconds mymaster 60000
# 同一个master,开始新的故障转移的时间(将是上一次的两倍)
# 若slave链接到错误的master超过这个时间后slave将被从新链接到正确的master
# 取消正在进行中的故障转移等待时间
# 按照parallel-syncs指定的配置进行复制的时间,超时候将再也不受parallel-syncs的限制
sentinel failover-timeout mymaster 180000
# 发生故障转移后,同时进行同步的副本数量
sentinel parallel-syncs mymaster 1

集群

Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。

Redis集群并不支持处理多个keys的命令(如mset),由于这须要在不一样的节点间移动数据,从而达不到像Redis那样的性能,在高负载的状况下可能会致使不可预料的错误.

Redis 集群经过分区来提供必定程度的可用性,在实际环境中当某个节点宕机或者不可达的状况下继续处理命令. Redis 集群的优点:

  • 自动分割数据到不一样的节点上。
  • 整个集群的部分节点失败或者不可达的状况下可以继续处理命令。

示意图:

image-20200602161912338

集群的数据分片

Redis 集群没有使用一致性hash, 而是引入了 哈希槽的概念.

Redis 集群有16384个哈希槽,每一个key经过CRC16算法校验后对16384取模来决定放置哪一个槽.集群的每一个节点负责一部分hash槽,举个例子,好比当前集群有3个节点,那么:

  • 节点 A 包含 0 到 5500号哈希槽.
  • 节点 B 包含5501 到 11000 号哈希槽.
  • 节点 C 包含11001 到 16384号哈希槽.

这种结构很容易添加或者删除节点. 好比若是我想新添加个节点D, 我须要从节点 A, B, C中得部分槽到D上. 若是我想移除节点A,须要将A中的槽移到B和C节点上,而后将没有任何槽的A节点从集群中移除便可. 因为从一个节点将哈希槽移动到另外一个节点并不会中止服务,因此不管添加删除或者改变某个节点的哈希槽的数量都不会形成集群不可用的状态.

客户端能够访问任意节点进行读写操做,若哈希槽不在当前访问的节点redis会自动的将指令移动到相关节点上;

主从复制模型

为了使在部分节点失败或者大部分节点没法通讯的状况下集群仍然可用,集群使用了主从复制模型,每一个节点都会有至少一个slave;

集群在没有slave的状况下,若是某个节点故障了,那么整个集群就会觉得缺乏一部分槽而不可用.

然而若是在建立集群时为每一个节点都添加了从节点,在某个节点故障后,其从节点将被选举为新的主节点,整个集群就不会因找不到槽而不可用,固然若某个节点与其全部子节点都故障了那么整个节点将不可用;

一致性保证

Redis 并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操做,主要有两方面缘由:

  • 主节点到从节点之间的指令复制是异步完成的,主从之间在某个时间点可能不一致
  • 出现网络分区,致使主节点可正常写入,可是从节点已经被其余分区节点选举为新的master,写入的数据将丢失

容错性

image-20200602165948571

当某master节点故障时,其余master节点将发起投票,若一半以上的master认为其不可用,则从该节点的从节点中(若存在)选举新的master;

若该master没有从节点,则集群将不可用

另外当集群一半以上的节点都不可用时则不管这些节点是否有从节点,集群当即不可用;

建立集群:

Redis在5.0版本时放弃了Ruby集群的方式,改成C语言编写的 redis-cli方式,使得集群的构建方式复杂度大大下降。

集群至少须要三个节点,每一个节点一个从节点总共为6个

  1. 配置:

    若要以集群方式运行,则须要按如下方式修改配置文件,以启用集群:

    cluster-enabled yes

    在实际开发中仍然建议使用单独的虚拟机来部署全部的redis节点,下例为了简化操做,在同一台虚拟机上搭建集群来进行测试:

  2. 添加上述配置后将bin目录复制6分名称为7001-7006,端口从7001-7006

    image-20200602174538250

  3. 分别启动6个redis示例

    image-20200602174555152

  4. 使用redis-cli建立集群

    redis-cli --cluster create 10.211.55.9:7001 10.211.55.9:7002 10.211.55.9:7003 10.211.55.9:7004 10.211.55.9:7005 10.211.55.9:7006 --cluster-replicas 1

    过程当中集群将重写配置文件,需输入yes确认

    建立完成后提示以下信息:

    ![image-20200602174849244](/Users/jerry/Library/Application Support/typora-user-images/image-20200602174849244.png)

    能够看到,集群自动为每一个master平均分配了哈希槽,而且设置了一个slave

  5. 链接集群

    ./redis-cli -h 10.211.55.9 -p 7001 -c
    # 参数 -c 即表示链接到集群
  6. 查看集群状态

    cluster info
  7. 查看节点信息,包括ip,port,id,槽,主/从;

    cluster nodes

添加节点

Redis集群支持动态扩展,期间redis可正常响应客户端访问

  1. 首先制做新的redis实例端口为7007并启动

  2. 添加到集群中

    ./redis-cli --cluster add-node 10.211.55.9:7007 10.211.55.9:7001

    添加成功:

    image-20200602180930266

  3. 新的节点默认做为master,可是该master没有分配槽位,使用前必须分配哈希槽

    下例表示从id为cc2e48268ccdd52d1c7840c8f9d2d7f15cc74c1b的节点移动1000个槽到cda3828e42e23dcbdb141db2fed221bc07c59f65节点

    ./redis-cli --cluster reshard 10.211.55.9:7001 --cluster-from cc2e48268ccdd52d1c7840c8f9d2d7f15cc74c1b --cluster-to cda3828e42e23dcbdb141db2fed221bc07c59f65 --cluster-slots 1000
    #--cluster-from  来源节点 多个以前用逗号隔开, all 表示从全部节点中平均分配
    #--cluster-to    目标节点
    #--cluster-slots 1000  移动哈希槽数量
  4. 为新的master添加从节点

  • 再次启动一个新的实例

    • 添加至集群并指定为某master的slave

      ./redis-cli --cluster add-node 10.211.55.9:7008 10.211.55.9:7001 --cluster-slave --cluster-master-id cda3828e42e23dcbdb141db2fed221bc07c59f65
      #--cluster-slave 指定新节点做为slave
      #--cluster-master-id 指定新节点的master

删除节点

  1. 删除从节点7008

    ./redis-cli --cluster del-node 10.211.55.9:7008 887d2f115f6a94bda86863576d73a131f12229d5
    #指定集群host:port  和要删除的节点id
  2. 将主节点的哈希槽分配给其余的主节点

    /redis-cli --cluster reshard 10.211.55.9:7001 --cluster-from cda3828e42e23dcbdb141db2fed221bc07c59f65 --cluster-to cc2e48268ccdd52d1c7840c8f9d2d7f15cc74c1b
  3. 删除主节点

    ./redis-cli --cluster del-node 10.211.55.9:7007 887d2f115f6a94bda86863576d73a131f12229d5
    #指定集群host:port  和要删除的节点id
  4. 哈希槽均衡,该操做检查各节点的槽均衡状况,若差别较大则自动从新分配

    ./redis-cli --cluster rebalance 10.211.55.9:7001