Redis中的高可用方案其实有以下集中:

  • 1、Redis主从复制(一主一从,一主多从)
  • 2、Redis主从复制+哨兵模式(比如:一主+两从+三哨兵)
  • 3、Redis集群模式(下一篇文章编写)
  • 4、Redis集群+主从模式(下一篇文章编写)

Redis主从复制,主要是为了做读写分离,写数据从主库操作,读数据从从库读取。但是普通的主从如果主服务器挂了,从服务器不会自动切换为主服务器。

Redis主从复制 +哨兵模式就是解决主服务器挂掉之后,哨兵监控到挂了之后,重新发起选举操作,让某台从服务器变为主服务器。从而继续提供服务。

Redis集群模式,就是将数据分片存储到不同的服务器,真正的达到了分布式存储,不像主从复制一样,存在存储上限。但是集群之后每个节点如果挂掉,存储的数据就会丢失。

Redis集群+主从模式,集群的每一个节点下面,都可以挂多台从服务器,从而让每个节点都是高可用的。

这些模式后面会一一讲到。

Redis是支持主从复制的,Redis开启主从复制的方式

  • 1、通过执行slaveof命令(Redis5.0之后改成了replicaof)开启主从复制.
  • 2、在配置文件中配置slaveof(Redis5.0之后改成了replicaof)开启主从复制

主从复制部署图

首先我们需要安装主服务器redis

下载编译redis

可以设置后台启动,密码等,因为redis不设置密码,并且开放到外网的话是非常危险的,很容易导致被黑客攻击。所以我们这里会设置密码

启动主从

查看进程

主从测试

主从复制操作就结束了,接下来看一下主从复制到底是个什么原理。

配置主从很简单,主服务器不用做过多的变更。

当客户端向从服务器发送slaveof(replicaof)主机地址(127.0.0.1) 端口(6379)时,从服务器将主服务器ip 端口保存到redisServer的masterhost和masterport中。

从服务器向发送slaveof命令的客户端返回一个OK,表示复制指令已经被接受,而实际上复制是在返回OK之后进行的。

从服务器(slave)保存主服务器(master)信息之后,就会与master建立连接。用于传输命令/RDB文件,接受来自master传播过来的写命令。

主服务器接受(accept)了从服务器的连接后,会创建相应的客户端状态,就相当于从服务器是主服务器的客户端。

slave向master发送ping命令,master回应一个pong,表示正常,返回错误,则表示master不正常,返回timeout,说明网络超时。

如果主服务器没有使用requirepass设置密码,则无需设置masterauth(主服务器密码)。

如果主服务器使用requirepass设置密码了,则需要设置masterauth(主服务器密码),跟requirepass的值保持一致。或者使用auth向主服务器发送命令。

在验证密码结束之后,从服务器将会执行命令replconf 监听端口,向主服务器发送从服务器监听的端口号。

同步数据指的是第一次开启主从复制之后,会把已经存在的数据一次性全部同步给从服务器(包括全量和增量同步),同步结束之后,每执行一个命令就会斤西瓜同步,也就是命令传播阶段。

Redis 2.8之前只有全量同(使用sync命令同步, redis 2.8之后使用psync命令代替sysnc)步,之后就增加了增量同步。

Redis2.8之前的数据同步

  • 1、通过从服务器发送sync命令给主服务器
  • 2、主服务器生成rdb文件并发送给从服务器,同时发送发送期间产生的写命令发送给从服务器
  • 3、从服务器清空之前的数据,然后解析执行RDB文件

Redis2.8之前的命令传播

同步完成之后,主服务器执行写命令,该命令发送给从服务器并执行,使主从保持一致

这种方式,没有增量和全量的说法,每次从服务器同步数据时,都会先清空从服务器原来的所有数据。

Redis2.8之后的数据同步

Redis 2.8之后使用psync命令,它具备了全量同步和增量同步模式。 只有从服务器第一次连接主服务器时才会全量同步。断线重连有可能触发增量和全量(断开时还没同步数据,就相当于重新全量同步)。

全量同步

  • 1、同步快照阶段:master创建并发送rdb快照给slave,slave载入并解析rdb文件。master将此阶段产生的新写命令存储到缓冲区。
  • 2、同步写缓冲区阶段:master向slave同步在缓冲区的写操作命令。
  • 3、同步增量阶段:master向slave同步写操作命令

增量同步

Redis的增量同步主要是指slave完成初始化之后开始正常工作时,master发生的写操作同步到slave的过程,一般是,master每执行一个写命令,就发送给slave相同的命令,然后slave执行,也就是命令传播。

在命令传播阶段,从服务器会以每秒一次的频率向主服务器发送命令。

replconf ack作用

  • 1、检查主从的连接状态,向主服务器发送info replication命令,列出从服务器列表,可以看出最后一次发送命令的时间。和当前时间超过1表示超过1s没有连接上,就判定连接有故障。
  • 2、辅助实现min-slaves,redis可盈通过配置防止主服务器在不安全情况下执行写命令。

从服务器满足以上任意请求,主服务器就会拒绝执行写命令。

  • 3、检测命令丢失
    • 因为网络故障,主服务器传播给从服务器的命令可能丢失,从服务器发送replconf ack时,主服务器发现从服务器当前的复制偏移量少于自己的偏移量,然后主服务器根据从服务器提交的偏移量,在复制积压缓冲区中找到从服务器缺少的数据,并将这些数据补发给从服务器。

哨兵(sentinel)是Redis的高可用性(High Availability)的解决方案

由一个或多个sentinel实例组成sentinel集群可以监视一个或多个主服务器和多个从服务器

当主服务器进入下线状态时,sentinel可以将该主服务器下的某一从服务器升级为主服务器继续提供服务,从而保证redis的高可用性

编辑slave2中的redis.conf文件,修改端口为6381.

准备哨兵

修改sentinel.conf

启动主从和哨兵

查看启动状态

Sentinel是一个特殊的Redis服务器,它不会进行持久化,sentinel实例化后,每个sentinel会跟主服务器创建两个连接。一个是命令连接,一个是订阅连接。

  • 1、命令连接:用于向主服务器发送命令并接受响应
  • 2、订阅连接:用于订阅主服务器的--sentinel--频道

Sentinel默认每10s一次,向被监控的主服务器发送info命令,获取主服务器和从服务器列表的信息。

可以看出,我们有两台从服务器,端口好分别是6380,6381.

如果Sentinel发现主服务器有新的从服务器加入主从,Sentinel还会向从服务器建立命令连接和订阅连接。在命令连接建立之后,Sentinel还是默认10s一次,向从服务器发送info命令,并记录从服务器的信息。

sentinel以订阅方式向主从发送信息

默认情况下,Sentinel每2s一次,向所有被监视的主/从服务器所订阅的—sentinel—:hello频道上发送消息,消息中会携带Sentinel自身的信息和主服务器的信息.

sentinel接受主从服务器的频道消息

当Sentinel与主服务器或者从服务器建立起订阅连接之后,Sentinel就会通过订阅连接,向服务器发送以下命令。

Sentinel彼此之间只创建命令连接,而不创建订阅连接.因为Sentinel通过订阅主服务器或从服务器,就可以感知到新的Sentinel的加入,而一旦新Sentinel加入后,相互感知的Sentinel通过命令连接来通信就可以了

Sentinel每秒一次向所有与它建立了命令连接的实例(主服务器、从服务器和其他Sentinel)发送PING命令。实例在down-after-milliseconds毫秒内返回无效回复(除了+PONG、-LOADING、-MASTERDOWN外),或实例在down-after-milliseconds毫秒内无回复(超时),Sentinel就会认为该实例主观下线(SDown)。

当一个Sentinel将一个主服务器判断为主观下线后,Sentinel会向同时监控这个主服务器的所有其他Sentinel发送查询命令。判断其他sentinel去尝试连接master,根据结果判断它们是否也认为主服务器下线。

如果达到Sentinel配置中的quorum数量的Sentinel实例都判断主服务器为主观下线,则该主服务器就会被判定为客观下线(ODown)。

当一个主服务器被判定为客观下线后,监视这个主服务器的所有Sentinel会通过选举算法(raft),选出一个Leader Sentinel去执行failover(故障转移)操作。

选举使用的是raft算法,raft算法就是专门来解决分布式一致性问题的一种算法。

Raft描述的节点共有三种状态:Leader, Follower, Candidate。Raft协议将时间切分为一个个的Term(任期),可以认为是一种“逻辑时间”。

raft算法

Raft采用心跳机制触发Leader选举,系统启动后,全部节点初始化为Follower,term为0。

节点如果收到了RequestVote或者AppendEntries,就会保持自己的Follower身份。

节点如果一段时间内没收到AppendEntries消息,在该节点的超时时间内还没发现Leader,Follower就会转换成Candidate,自己开始竞选Leader。

一旦转化为Candidate,该节点立即开始下面几件事情:

  • 1、增加自己的term
  • 2、启动一个新的定时器
  • 3、给自己投一票
  • 4、向所有其他节点发送RequestVote,并等待其他节点的回复

如果在计时器超时前,节点收到多数节点的同意投票,就转换成Leader。同时向所有其他节点发送AppendEntries,告知自己成为了Leader。

每个节点在一个term内只能投一票,采取先到先得的策略,Candidate前面说到已经投给了自己, Follower会投给第一个收到RequestVote的节点。

Raft协议的定时器采取随机超时时间,这是选举Leader的关键,在同一个term内,先转为Candidate的节点会先发起投票,从而获得多数票。

选举流程

  • 1、某Sentinel认定master客观下线后,该Sentinel会先看看自己有没有投过票,如果自己已经投过票给其他Sentinel了,在一定时间内自己就不会成为Leader。
  • 2、如果该Sentinel还没投过票,那么它就成为Candidate
  • 3、Sentinel需要完成几件事情
    • 更新故障转移状态为start
    • 当前epoch加1,相当于进入一个新term,在Sentinel中epoch就是Raft协议中的term
    • 向其他节点发送 is-master-down-by-addr 命令请求投票。命令会带上自己的epoch。
    • 给自己投一票(leader、leader_epoch)
  • 4、当其它哨兵收到此命令时,可以同意或者拒绝它成为leader(通过判断epoch)
  • 5、Candidate会不断的统计自己的票数,直到他发现认同他成为Leader的票数超过一半而且超过它配置的quorum,这时它就成为了Leader。
  • 6、其他Sentinel等待Leader从slave选出master后,检测到新的master正常工作后,就会去掉客观下线的标识。

选举之后,新的master出现了,那么就需要将原本指向旧master的从服务器,指向新的master。

  • 1、 它会将失效Master的其中一个Slave升级为新的Master , 并让失效Master的其他Slave改为新的Master
  • 2、当客户端试图连接失效的Master时,集群也会向客户端返回新Master的地址,使得集群可以使用现在的Master替换失效Master
  • 3、Master和Slave服务器切换后,Master和Slave的redis.conf和sentinel.conf配置文件会被修改,就是指向新的master.

Redis的主从+哨兵模式,在我们数据量比较小的时候,还是个比较实用的高可用方案。它的优势有哪些呢?

  • 1、主服务器负责写,从服务器负责读,实现了读写分离。
  • 2、既然读写分离了,通常Redis的读请求远大于写请求,所以多个从节点提升了读性能和读的吞吐量。
  • 3、主从复制解决的主从数据一致性问题
  • 4、从服务器是主服务器的一个备份,相当于多了一层备份。
  • 5、主机宕机,从服务器可读不可写,从服务器不可切换成主服务器,而哨兵模式解决了该问题。

除了总的存储数据受单机限制,主从+哨兵已经可以保证高性能,高可用了。

本人还写了Redis的其他相关文章,有兴趣的可以点击查看!

  • <<Redis弱事务性与Lua脚本原子性分析>>

  • <<Redis持久化机制分析>>

  • <<Redis的事件处理机制分析>>

  • <<Redis客户端和服务端如何通信?>>

  • <<Redis的淘汰机制分析>>

  • <<Redis的底层数据结构分析>>

  • <<Redis的8种数据类型,什么场景使用?>>

  • <<缓存是什么?缓存有哪些分类?使用它的代价是什么?>>

  • <<缓存的6种常见的使用场景>>