前言
上一篇我们讲解了redis如何使用docker去搭建主从模式+哨兵的一个高可用架构,那么我们也基本了解了他给我们带来的好处,但是所有事物都是双面性的,他给我们带来好处的同时也会给我们带来一些问题或者缺点,上一篇前言我们提到了redis主从复制中间的一个数据一致性问题,也讲到了如果使用主从模式就需要项目去接受短时间的脏数据问题,那本文就着重讲解一下redis的master(主机)是如何与从机保持数据一致以及哨兵又是如何去监听master节点的健康以及故障转移的呢!
Master(主机)和Slave(从机)的数据一致性问题
当我们使用主从模式去实现redis的高可用架构的时候,同时会启动多个redis实例,然后可以根据配置文件来确定那个是主机那个是从机,在主机宕机后可以通过手动或者哨兵模式(Sentinel)来监控推举一个从机来当新的主机!那么我们如何保证从机与主机的数据一致性,并且可以实现读写分离(因为redis读多写少所以我们可以将他读写分离 - > 主机(写)从机(读))?
基于reids的两种持久化来实现(以下为redis2.8之后的 PSYNC方式;2.8之前每次都发送sync命令(完整重(zhong)同步),来确保数据统一,太消耗资源);
-
全量同步
Redis全量复制一般发生在Slave(从机)初始化阶段,这时Slave(从机)需要将Master(主机)上的所有数据都复制一份。具体步骤如下:- 从机向主机发送sync命令
- 主机收到sync命令后,会执行BGSAVE命令生成RDB文件(主机数据)并使用缓冲区记录此后执行的所有写命令;
- 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
- 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照
-
增量同步(2.8 版本之后的优化 ,变成了 第一次发送为全量同步,之后只发送增量数据)
Redis增量同步为在redis主机运行时发生了写操作,然后将该写操作的命令发送给从机使从机同时也执行该写操作,具体如下:- 主服务器全量快照发送完毕后开始向从服务器发送缓冲区中的写命令;
- 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令
完成上面几个步骤后就完成了从服务器数据初始化的所有操作,从服务器此时可以接收来自用户的读请求。然后主机则负责写操作,从机负责读操作;但是毕竟项目总会有突发情况,那redis又是如何处理这些突发情况的呢,看以下分析!
问题 1 : 如果redis从服务器短线重连后怎么确定他的数据与主机数据的同步情况?
- 在2.8后redis提供了PSYNC优化了短线重连,同时有了积压缓存区;在主机与从机之间记录一个复制偏移量(就是在主机向从机发送增量数据时会在该数据的最后一个写命令后记录一个标签也就是复制偏移量)同时将该复制偏移量发送给从服务器!(积压缓存区:数据库的写命令会存储到积压缓冲区)在从服务器断线重连后,会携带复制偏移量向主服务器发送增量数据获取命令;
- 主机收到复制偏移量后会在数据积压缓存区查找该复制偏移量,来确定该从机最后一次成功获取增量数据的位置,并发送之后的增量数据来确保从机断线重连后数据同步;
问题2 : 那么如何保证从服务器在断线重连期间主服务器发生变更(主服务器发生宕机后会挑选一个从服务器来做主机)然而这个新的主机可不知道从服务器的同步情况(新主机的积压缓存区中不存在他的复制偏移量)
- 也是在2.8版本之后每个主机都有一个运行ID来作为标识:
- 当从服务器断线重连后在与主服务器联系之前会将自己断线之前所拿的主机ID与当前主机ID进行对比来确定该次同步是进行全量同步还是增量同步
- 如果不匹配则进行全量同步,如果匹配则使用复制偏移量来获取他所需要的增量数据;
哨兵模式(Sentinel)
作用
哨兵模式用于配合redis主从或者集群来使用并达成高可用的架构,主要作用就是用来监视redis主节点的运行情况,并及时故障转移;
故障转移 - 在主服务器发生宕机的时候进行推举命令在该主服务器下面的从服务器中挑选一个从机来接替以前的主机进行工作
工作原理
pingpong机制
- 哨兵服务会每秒向redis主节点与从节点发送一次ping命令
- 如果有一个节点在距离最后一次有效回复PING命令的时间超过规定数值,那么就会主观判断为该节点已宕机(超时时间可以配置,具体看我上一章)
- 如果该节点为从节点那么会直接将他剔除,不在提供读操作
- 如果为主节点,那么就会将该主节点标记为主观下线(也就是不太确定该节点是否已下线)状态,则正在监视该主节点的所有哨兵将会每秒对该主节点发送ping命令来确定该节点是否为主观下线,如果有多数(该数值在配置文件中配置,一般为半数以上)哨兵将该节点标记为主观下线的话,将会将该标记改为客观下线(确定该主节点已下线)
- 然后同时执行推举,将该主服务器下的多数从服务器中推举一个来作为主服务器;
- 推举成功后会通知其他从节点与新主节点进行数据同步
- 然后新主节点也会开始进行主要的写操作
以上就是哨兵的完整监听以及推举的步骤,在上一章Redis系列 使用docker部署redis主从模式+哨兵监听一文中的末尾测试的时候,我大致讲解了一下当主节点下线之后哨兵的日志文件打印情况,大家有兴趣可以去看一下!
问题1 : 如果哨兵的网络不好或者服务器压力较大,导致哨兵的ping主节点的命令并没有在规定时间内返回,并不是因为主节点下线才导致的ping命令失败,这样子形成的推举岂不是很严重的错误推举么
答 : 这也是为什么我们需要搭建的不是哨兵本身,而是搭建哨兵的时候集群则是起步状态,一般我们搭建哨兵集群以3个哨兵服务为基准,而且最好不要搭建在同服务器,哨兵的数量越多以及配置的法人数越高(法人数 - 就是当多少个哨兵ping不通以后就修改为客观下线)那么误判的几率也会随之下降,但是也是需要适当,毕竟哨兵他会时时刻刻的使用ping命令去监听redis节点的健康状态,过多的哨兵会让推举和监听成为服务器新的负担!
问题2 : 那如果我使用哨兵去监听主节点,当主节点下线了他推举了一个新节点,我项目中配置的还是旧节点地址,那项目不还是请求失败么,那这哨兵还有什么作用啊
答 : Redis在实现高可用+哨兵模式后,我们的项目就不可以去连接redis的主节点了,只能去连接哨兵然后通过哨兵去获取新的主节点地址并请求,下面我告诉大家如何SpringBoot去集成哨兵,很简单
- 导入操作redis的pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 修改yml配置文件
spring:
redis:
password: 123456 #如果有密码的话 这里就redis的密码
sentinel:
master: mymaster # 主机名称
nodes: # 哨兵的ip地址:端口,改成自己的
- 120.27.239.99:26381
- 120.27.239.99:26382
- 120.27.239.99:26382
这样子就好了,项目的redis写请求就可用通过哨兵去访问到新的主节点了