Redis—哨兵模式

概念

Redis的主从复制模式下,一旦主节点由于故障不能提供服务,需要人工进行主从切换,同时大量的客户端需要被通知切换到新的主节点上,对于上了一定规模的应用来说,这种方案是无法接受的,于是Redis从2.8版本开始提供了Redis Sentinel(哨兵)模式来解决这个问题。

由于对 Redis 的许多概念都有不同的名词解释,所以在介绍 Redis Sentinel 之前,先对几个名词概念进行必要的说明,如图所示。

Redis Sentinel是Redis的高可用实现方案,在实际的生产环境中,对提高整个系统的高可用是非常有帮助的。
哨兵机制,是通过独立的进程来体现的,和之前redis-server是不同的进程。Redis Sentinel不负责存储数据,只是对其他的redis-server进程起到监控的效果。


Redis的主从复制模式可以将主节点的数据改变同步给从节点,这样从节点就可以起到两个作用:

  1. 作为主节点的一个备份,一旦主节点出现故障不可达的情况,从节点可以作为后背"顶"上,并且保证数据尽量不会丢失。
  2. 从节点可以分担主节点上的读压力,让主节点只承担写请求的处理,将所有的读请求负载均衡到各个从节点上。

但是主从复制模式并不是万能的,它同样遗留下几个问题:

  1. 主节点发生故障时,进行主备切换的过程是复杂的,需要完全的人工参与,导致故障恢复时间无法保证。
  2. 主节点可以将读压力分散出去,但是写压力/存储压力是无法被分担的,还是受到单机的限制。
    其中第一个问题是高可用问题,即 Redis 哨兵主要解决的问题。第二个问题是属于存储分布式的问题,留给 Redis 集群去解决,我们主要讨论第一个问题。

人工恢复主节点故障

在实际的开发中,对于服务器后端开发,监控程序是非常重要的。服务器首先要求有比较高的可用性,要无时无刻运行,但是长期运行下去,总会有一些"意外",具体啥时候出现意外,我们也不知道。因此需要写一个程序,用程序来盯着服务器端运行状态,这个程序可以叫做监控程序,当出现故障时,可以给程序员进行"报警",通知程序员服务器出现了问题。

Redis 主节点故障后需要进行的操作如下:

  1. 运维人员通过监控系统,发现主节点故障宕机。
  2. 运维人员从所有节点中,选择一个从节点执行slaveof no one,使其作为新的主节点。
  3. 运维人员让剩余从节点执行slaveof {newMasterIP} {newMasterPort},从新主节点开始数据同步。
  4. 更新应用放连接的主节点信息到{newMasterIP} {newMasterPort}
  5. 如果原来的主节点恢复,执行slaveof {newMasterIP} {newMasterPort}让其成为一个从节点。
    上述过程可以看到基本需要人工介入,无法被认为架构是高可用的。而这就是Redis Sentinel所要做的。

哨兵

当主节点出现故障时,Redis Sentinel 能⾃动完成故障发现和故障转移,并通知应用方,从而实现真正的⾼可⽤。

Redis Sentinel 是⼀个分布式架构,其中包含若⼲个 Sentinel 节点和 Redis 数据节点,每个Sentinel 节点会对数据节点和其余 Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线表示。如果下线的是主节点,它还会和其他的 Sentinel 节点进行 "协商",当大多数 Sentinel 节点对主节点不可达这个结论达成共识之后,它们会在内部 "选举" 出⼀个领导节点来完成⾃动故障转移的⼯作,同时将这个变化实时通知给 Redis 应用方。整个过程是完全自动的,不需要⼈⼯介入。整体的架构如图所示:

Redis Sentinel相比于主从复制模式是多了若干(建议保持奇数)Sentinel 节点⽤于实现监控数据节点,哨兵节点会定期监控所有节点(包含数据节点和其他哨兵节点)。如图所示这三个哨兵进程会监控现有的redis masterslave,(监控:这些进程之间,会建立TCP长连接,通过这样的长连接,定期发送心跳包),借助上述的监控机制,就可以及时发现某个主机是否挂了,如果是从节点挂了,是没有关系的。针对主节点故障的情况,故障转移流程大致如下:

  1. 主节点故障,从节点同步连接中断,主从复制停止。
  2. 哨兵节点通过定期监控发现主节点出现故障。哨兵节点与其他哨兵节点进行协商,达成多数认同主节点故障的共识。这主要是防止出现故障的不是主节点,而是发现故障的哨兵节点。这是因为网络传输数据时容易出现抖动或者延迟丢包的。
  3. 哨兵节点之间使⽤ Raft 算法选举出⼀个领导⻆⾊,由该节点负责后续的故障转移⼯作。
  4. 哨兵领导者开始执行故障转移:从节点中选择⼀个作为新主节点;让其他从节点同步新主节点;通知应用层转移到新主节点。

redis哨兵节点只有一个也是可以的。但是不建议只有一个哨兵节点,如果只有一个的话,它自身也是可能会出现问题的 ,万一这个哨兵节点挂了,后续redis节点也挂了,就无法进行自动恢复过程了;还要一个情况是出现误判的概念也比较高了,因为网络的问题。

通过上⾯的介绍,可以看出 Redis Sentinel 具有以下几个功能:

  • 监控: Sentinel 节点会定期检测 Redis 数据节点、其余哨兵节点是否可达。
  • 故障转移: 实现从节点晋升(promotion)为主节点并维护后续正确的主从关系。
  • 通知: Sentinel 节点会将故障转移的结果通知给应用方。

安装部署哨兵模式

准备工作

为了演示一个完整的Redis分布式架构,我们使用Docker容器来完成创建。总够要创建6个容器,其中的三个容器作为redis的数据节点(一个主节点,两个从节点),另外三个容器作为redis的哨兵节点。使用docker的原因是大部分都只有一台主机。接下来,就根据步骤来来完成整个分布式系统的创建。

  1. 安装 dockerdocker-compose

    ubuntu

    apt install docker-compose

    centos

    yum install docker-compose

  2. 停⽌之前的 redis-server

    停⽌ redis-server

    service redis-server stop

    停⽌ redis-sentinel 如果已经有的话.

    service redis-sentinel stop

  3. 使⽤ docker 获取 redis 镜像。

    docker pull redis:5.0.9

如果这步失败的话,说明没有配置过镜像加速器的地址。先打开vim /etc/docker/daemon.json

如果没有这个文件, 就先创建一下, 如果有就直接写入下列代码:

复制代码
{
    "registry-mirrors": [
        "https://docker.m.daocloud.io",
        "https://dockerhub.timeweb.cloud",
        "https://huecker.io"
  ]
}

完成之后,再重新获取redis镜像。

编排redis主从节点

先找到一个合适的位置创建两个文件夹,redis-data用来存放数据节点,redis-sentinel用来存放哨兵节点。每个目录下面都有docker-compose.yml文件,用于进行容器的编排。这个文件名称是指定的,不能更改。

  • 编写 redis-data/docker-compose.yml文件:

    version: '3.7'
    services:
    master:
    image: 'redis:5.0.9'
    container_name: redis-master
    restart: always
    command: redis-server --appendonly yes
    ports:
    - 6379:6379
    slave1:
    image: 'redis:5.0.9'
    container_name: redis-slave1
    restart: always
    command: redis-server --appendonly yes --slaveof redis-master 6379
    ports:
    - 6380:6379
    slave2:
    image: 'redis:5.0.9'
    container_name: redis-slave2
    restart: always
    command: redis-server --appendonly yes --slaveof redis-master 6379
    ports:
    - 6381:6379

其中6380和6381是在主从复制章节配置过的两个从节点的端口号。

  • 在目录redis-data中执行:

    docker-compose up -d

如果启动后发现前面的配置有误, 需要重新操作, 使用 docker-compose down即可停止并删除刚才创建好的容器。

  • 查看运行日志

    docker-compose logs

上述操作必须保证⼯作目录在 yml 的同级⽬录中, 才能⼯作。

  • 验证

连接主节点redis-cli -p 6379

连接从节点redis-cli -p 6380

编排 redis-sentinel 节点

  1. 编写 redis-sentinel/docker-compose.yml。注意: 每个⽬录中只能存在⼀个 docker-compose.yml ⽂件。

    version: '3.7'
    services:
    sentinel1:
    image: 'redis:5.0.9'
    container_name: redis-sentinel-1
    restart: always
    command: redis-sentinel /etc/redis/sentinel.conf
    volumes:
    - ./sentinel1.conf:/etc/redis/sentinel.conf
    ports:
    - 26379:26379
    sentinel2:
    image: 'redis:5.0.9'
    container_name: redis-sentinel-2
    restart: always
    command: redis-sentinel /etc/redis/sentinel.conf
    volumes:
    - ./sentinel2.conf:/etc/redis/sentinel.conf
    ports:
    - 26380:26379
    sentinel3:
    image: 'redis:5.0.9'
    container_name: redis-sentinel-3
    restart: always
    command: redis-sentinel /etc/redis/sentinel.conf
    volumes:
    - ./sentinel3.conf:/etc/redis/sentinel.conf
    ports:
    - 26381:26379
    networks:
    default:
    external:
    name: redis-data_default

  2. 创建配置文件
    创建 sentinel1.conf sentinel2.conf sentinel3.conf。三份⽂件的内容是完全相同的。docker存储卷要用绝对路径,所以要根据自己的主机情况,填入绝对路径。
    配置文件内容如下:

    bind 0.0.0.0
    port 26379
    sentinel monitor redis-master redis-master 6379 2
    sentinel down-after-milliseconds redis-master 1000

我们来看一下sentinel monitor内容

复制代码
sentinel monitor 主节点名 主节点ip 主节点端⼝ 法定票数
  • 主节点名:这是哨兵内部自己起的名字。
  • 主节点IP:部署redis-master的设备IP。此处由于是使用docker,可以直接写docker的容器名,会被自动DNS成对应的容器IP
  • 主节点端口号。
  • 法定票数:哨兵需要判断主节点是否挂了,但是有的时候可以因为特殊情况,比如主节点仍然工作正常,但是哨兵节点自己网络出现了问题,无法访问到主节点了。此时就可能会使该哨兵节点认为主节点下线,出现误判。使用投票的方式来确定主节点是否挂了是更稳妥的做法,需要多个哨兵都认为主节点挂了,票数>=法定票数之后,才会真的认为主节点是真的挂了。
    理解 sentinel down-after-milliseconds
    主节点和哨兵之间通过心跳包来进行沟通。如果心跳包在指定的时间内还没回来, 就视为是节点出现故障。
  1. 启动所有容器docker-compose up -d
    如果启动后发现前⾯的配置有误, 需要重新操作, 使⽤ docker-compose down 即可停止并删除刚才创建好的容器。启动后,打开刚刚写的配置文件:

可以看到,除了刚开始写的内容,又新增了很多内容。

重新选举

⼿动把 redis-master干掉,来模拟主节点挂了。

复制代码
docker stop redis-master

接着使用docker compose logs,观察哨兵日志信息。

可以看到哨兵发现了主节点 sdown, 进⼀步的由于主节点宕机得票达到 3/2 , 达到法定得票, 于是master 被判定为 odown。

  • 主观下线 (Subjectively Down, SDown): 哨兵感知到主节点没心跳了,判定为主观下线。
  • 客观下线 (Objectively Down, ODown): 多个哨兵达成⼀致意见,才能认为 master 确实下线了。
    进入6379端口的数据节点和进入6381端口的数据节点。

可以看到6381成为了主节点,而6379成为看从节点。


⼿动把 redis-master 启动起来docker start redis-master

  • Redis 主节点如果宕机, 哨兵会把其中的⼀个从节点, 提拔成主节点。
  • 当之前的 Redis 主节点重启之后, 这个主节点被加⼊到哨兵的监控中, 但是只会被作为从节点使⽤.

选举原理

  1. 主观下线
    redis-master 宕机,,此时 redis-master 和三个哨兵之间的心跳包就没有了。此时, 站在三个哨兵的⻆度来看, redis-master 出现严重故障. 因此三个哨兵均会把 redis-master 判定为主观下线 (SDown)

  2. 客观下线
    此时, 哨兵 sentenal1, sentenal2, sentenal3均会对主节点故障这件事情进行投票。当故障得票数 >= 配置的法定票数之后:

    sentinel monitor redis-master 172.22.0.4 6379 2

在这个地⽅配置的 2 , 即为法定票数。

此时意味着 redis-master 故障这个事情被做实了. 此时触发客观下线 (ODown)

  1. 选举出哨兵的leader

接下来需要哨兵把剩余的 slave 中挑选出⼀个新的 master. 这个⼯作不需要所有的哨兵都参与. 只需要选出个代表 (称为 leader), 由 leader 负责进⾏ slave 升级到 master 的提拔过程。

这个选举的过程涉及到 Raft 算法:

  • 每个哨兵节点都给其它所有哨兵节点,发起一个拉票请求。
  • 收到拉票请求的节点,会回复一个拉票响应。响应的结果有两种可能,投或者不投。
  • 一轮投票完成之后,发现得票超过半数的节点,自动成为leader。如果出现平票的情况,就重新再投一次即可。这也是为啥建议哨兵节点设置为奇数个的原因。如果是偶数个,则增大了平票的概率,带来不必要的开销。
  • leader节点负责挑选一个slave成为新的master,当其它的sentinel发现新的master出现了,就说明选举结束了。
    简而言之, Raft 算法的核心就是 "先下手为强". 谁率先发出了拉票请求,谁就有更大的概率成为 leader。这⾥的决定因素成了 "⽹络延时"。网络延时本⾝就带有⼀定随机性.
  1. leader 挑选出合适的 slave 成为新的 master

挑选规则:

  • 比较优先级. 优先级高(数值小的)的上位。优先级是配置文件中的配置项( slave-priority 或者replica-priority )。
  • 比较 replication offset 谁复制的数据多,高的上位。
  • 比较 run id , 谁的 id 小, 谁上位。

当某个数据节点被选为master后:

  1. leader哨兵指定该节点执行slave no one,成为master
  2. leader哨兵指定剩余节点执行slave of,成为该节点的从节点

总结

上述过程, 都是 "⽆⼈值守" , Redis ⾃动完成的。这样做就解决了主节点宕机之后需要人工干预的问题,提高了系统的稳定性和可用性。

⼀些注意事项:

  • 哨兵节点不能只有⼀个。否则哨兵节点挂了也会影响系统可⽤性.
  • 哨兵节点最好是奇数个。方便选举 leader, 得票更容易超过半数.
  • 哨兵节点不负责存储数据。仍然是 redis 主从节点负责存储.
  • 哨兵 + 主从复制解决的问题是 "提⾼可⽤性", 不能解决 "数据极端情况下写丢失" 的问题.
  • 哨兵 + 主从复制不能提⾼数据的存储容量. 当我们需要存的数据接近或者超过机器的物理内存, 这样的结构就难以胜任了。
相关推荐
轻刀快马5 小时前
Redis 架构进阶:全景解析 RDB、AOF 与混合持久化机制
redis
Albert Edison9 小时前
【Redis】Centos7.9 安装 Redis 5 教程
数据库·redis·缓存
Steadfast_GG10 小时前
Redis中的通用命令
redis·缓存
小二·10 小时前
Redis 内存溢出(OOM)排查与恢复实战
数据库·redis·bootstrap
pqk6V6Vep10 小时前
Redis 分布式锁进阶第一篇讲解
数据库·redis·分布式
giaz14n9X10 小时前
Redis 分布式锁进阶第六十一篇
数据库·redis·分布式
JAVA面经实录91713 小时前
Redis 知识体系(完整版)
java·redis·nosql数据库·nosql
颜笑晏晏14 小时前
长输入短输出场景下的 SGLang 推理性能实测前缀缓存、PD 分离配比与参数调优
缓存·推理优化·sglang·ai infra·pd分离
ManageEngine卓豪14 小时前
数据库可观测性:MySQL与Redis监控核心监控指标与全栈运维解决方案
数据库·redis·mysql·数据库性能·数据库监控
真实的菜14 小时前
Redis 从入门到精通(十四):Redis 7.x 新特性全解 —— 系列收官之作
数据库·redis·缓存