当前位置:   article > 正文

redis 哨兵

redis 哨兵

前言

redis 主从复制模式下, 一旦主节点出现故障, 不能提供服务的时候, 就需要人工进行主从切换. 这是十分不靠谱的, 毕竟谁也不知道主节点什么时候出现故障, 这样就无法第一时间恢复服务. 所以 redis 从 2.8 版本开始提供了 哨兵(Redis Sentinel) 模式, 可以通过自动化手段来解决主节点出现故障的问题.

主从复制的问题

redis 主从复制是将主节点的数据同步给从节点, 这样从节点就拥有主节点的所有数据, 这样客户端就可以从从节点这边来读取数据, 来分担主节点所要承受的并发量. 但是主从复制也有一些问题:

  1. 主节点发生故障的时候, 这时整个 redis 服务就失去了写入数据的功能, 这时就需要程序员手动干预来将从节点切换成主节点.
  2. 主从复制可以将读的并发量分担给从节点, 但是写压力/存储压力是无法被分担的, 还是受到单机的限制.

其中第一个问题就是哨兵模式主要解决的问题, 而第二个问题是 redis 集群模式解决的问题, 本章主要讨论第一个问题.

怎么人工恢复故障主节点

首先程序员会先看看主节点挂的原因, 并且尝试能不能抢救, 如果知道原因并且容易恢复主节点的功能, 就直接将主节点恢复即可.

如果不好定位原因, 或者知道原因, 但是短时间之内不能恢复, 这时, 程序员就需要挑一个从节点, 设置成主节点:

  1. 在挑选出来的从节点上执行 slaveof no one, 使其脱离故障的主节点.
  2. 在其它的从节点上执行 slaveof 新主节点ip 新主节点端口, 使其连上新的主节点.
  3. 告知客户端(修改客户端的配置), 让客户端能够连接新的主节点, 用来完成修改数据的操作.

当之前挂了的主节点修好了之后, 就可以作为一个新的从节点, 加入到这个分布式系统中.

上述恢复操作涉及到了人工干预, 人工干预的过程也是有可能出错的, 如果配置的时候不小心配置错误了, 可能就会导致更严重的问题. 而且人工干预也是需要时间的, 在集群配置好之前, 整个 redis 服务都不能进行写操作, 这段时间多少都会造成损失.

所以, redis 引入了哨兵模式, 就能解决上述问题.

Redis Setinel 架构

如图所示, redis 哨兵模式也是加入了一个集合, 里面有若干个哨兵节点, 这几个哨兵节点会监控现有的 redis master 和 slave(建立 tcp 长连接, 并定期发送心跳包). 借助监控机制, 就可以及时发现某个主机是否挂了. 如果从节点挂了, 不会有多大影响, 并不影响整个集群的读写功能. 如果是主节点挂了, 哨兵就要发挥作用了.

  1. 此时一个哨兵节点发现主节点挂了, 还不够, 需要有多个哨兵节点来认同这个主节点挂了(防止单个哨兵误判).
  2. 如果主节点确实挂了, 这些哨兵节点中就会挑选出一个 leader, 由这个 leader 负责从现有的从节点中, 挑选一个作为新的主节点.
  3. 挑选出新的主节点之后, 哨兵节点就会自动控制该被选中的从节点, 执行 slaveof no one, 并且控制其他从节点, 修改主从结构到新的主节点上.
  4. 哨兵节点会自动通知客户端程序新的主节点是哪个, 并且后续客户端再进行写操作, 就会针对新的主节点进行操作了.

redis 哨兵核心功能:

  1. 监控: 监控主节点的状态, 当主节点出现故障, 及时做出处理
  2. 自动的故障转移: 自动的完成挑选从节点替换主节点操作
  3. 通知: 通知客户端新的主节点

如果只设置一个哨兵节点, 会出现的问题:

  1. 单个哨兵节点如果故障了, 就无法监控主节点的情况, 就无法进行自动恢复的过程了.
  2. 出现误判的概率高, 网络传输的数据容易出现抖动, 延迟或者丢包的, 如果只有一个哨兵节点, 出现上述问题的概率就很高, 就会导致单个哨兵节点出现误判, 影响比较大.

使用 docker 来配置哨兵结构

安装 docker

这里使用一台服务器来部署哨兵结构, 由于手动部署多个节点非常麻烦, 所以使用 docker 来配置更为轻松.

此处是基于 Ubuntu 环境进行配置

  1. 安装 docker 和 docker-compose
apt install docker-compose
  • 1
  1. 停止之前的 redis-server
service redis-server stop
  • 1
  1. 使用 docker 来获取 redis 镜像
docker pull redis:5.0.9
  • 1
编排 redis 主从节点
  1. 创建 docker 的配置文件 docker-compose.yml
mkdir redis
cd redis
mkdir redis-data
cd redis-data
vim docker-compose.yml
  • 1
  • 2
  • 3
  • 4
  • 5

配置如下:

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  1. 启动所有容器
docker-compose up -d
  • 1
编排 redis 哨兵节点
  1. 创建 docker 的配置文件 docker-compose.yml
cd redis
mkdir redis-sentinel
vim docker-compose.yml
  • 1
  • 2
  • 3

配置如下:

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  1. 创建哨兵节点的配置文件
    创建三个配置文件 sentinel1.conf, sentinel2.conf, sentinel3.conf, 其中三个配置文件的内容都相同:
bind 0.0.0.0
port 26379
sentinel monitor redis-master redis-master 6379 2
sentinel down-after-milliseconds redis-master 1000
  • 1
  • 2
  • 3
  • 4

解释一下配置文件中的配置:

  1. sentinel monitor 主节点名 主节点ip 主节点端口 法定票数
    • 其中, 法定票数的解释: 比如当工作时, 其中某个哨兵节点的网络出现问题, 但是主节点仍正常工作, 此时这个哨兵节点就可能产生误判, 认为主节点挂了. 所以哨兵模式是采用投票的方式来判断主节点是否挂了, 这个方案更为稳妥. 当某个哨兵节点发现主节点挂了之后, 就会发起投票, 当认为主节点挂了的票数 >= 法定票数的时候, 哨兵集群才真正认为主节点挂了, 从而采取后面进一步的操作.
  2. sentinel down-after-milliseconds 主节点ip 超时时间
    • 主节点和哨兵之间根据心跳包来进行沟通, 如果心跳包在指定时间之内还没有响应, 就认为主节点挂了.
  1. 启动所有容器
docker-compose up -d
  • 1

到这步, redis 哨兵结构就配置完成了.

观察哨兵模式的作用

此时已经将三个主从节点和三个哨兵节点都提起来了:

这时, 手动将主节点停掉, 来模拟主节点故障:

docker stop redis-master
  • 1

当主节点挂了之后, 哨兵节点就开始工作了, 观察哨兵节点的日志:

  • 在 redis-sentinel 目录下
    docker-compose logs
    
    • 1

由于三个节点的日志都大差不差, 只观察一个节点的日志即可:

可以看到, 当 master 节点挂了之后, 哨兵节点首先先发现主节点 sdown, 然后开始投票, 当票数达到 3/2 的时候, 达到法定票数, 于是 master 节点被判定为 odown.

  • sdown: 主观下线(SubjectivelyDown), 当前哨兵节点认为该主节点挂了.
  • odown: 客观下线(ObjectivelyDown), 多个哨兵节点都认为该节挂了, 达成了一直(达到法定票数), 此时才认为 master 节点确实挂了.

接下来, 哨兵节点就会挑选出一个节点来作为主节点, 如图中的 switch-master 操作

主从切换的具体流程

  1. 主观下线: 哨兵节点通过心跳包判定 redis 服务器是否正常工作, 如果心跳包在一定时间内没有响应, 说明 redis 服务器挂了. 此时还不能排除网络波动的影响, 因此只能单方面认为 redis 主节点挂了.

  2. 客观下线: 多个哨兵节点认为主节点挂了, 即认为主节点挂了的哨兵节点个数达到法定票数, 此时就认为是客观下线.

  3. 让多个哨兵节点选出一个 leader 节点, 由这个 leader 负责选出一个从节点作为新的主节点

    • 观察上述 sentinel1 的日志可以看到:通过刚刚的配置文件中的信息可以得知:

      sentinel1 的 id 为: bc48b2a2e35a8a0fedb95cd5095916f5c9b560db

      sentinel2 的 id 为: 7ca7cb5c9d0707e9558aa984fb647ebfb8d0223f

      sentinel3 的 id 为: ba6a0e8374a1efbc5837ce8301a9d0b498468ea7

      上述过程中, sentinel1 先给自己投了一票, 接着 sentinel2 给自己投了一票, sentinel3 给 sentinel2 投了一票, 此时 sentinel2 就为 leader.

  4. 此时 leader 选组完毕, leader 就需要挑选一个从节点来作为新的主节点.

    • 挑选规则:
      1. 比较优先级: 在配置文件中的 slave-priority 或者 replica-priority 可以设置优先级, 优先级高的就会胜出.
      2. 比较 offset: 比较从节点从主节点中同步数据的进度, offset 越大, 说明从主节点这边同步的数据越多, offset 大的节点会被挑选成为主节点
      3. run id: redis 节点启动时生成的随机数, 谁的 id 小, 谁就会被挑选.

当新的主节点指定好之后, leader 就会控制这个节点, 执行 slaveof no one, 成为 master, 再控制其它节点, 执行 slaveof, 让这些节点以新的 master 作为主节点.

小结

上述过程都是自动完成的, 这样就解决了主节点宕机之后需要人工干预的问题, 提高了系统的稳定性和可用性.

还需要注意一些事项:

  • 哨兵节点不能只有一个, 否则哨兵节点挂了也会影响系统的可用性.
  • 哨兵节点最好是奇数个, 方便选举 leader, 得票容易超过半数.
  • 哨兵节点不负责存储数据, redis 主节点负责存储.
  • 主从复制 + 哨兵解决的问题是 “提高可用性”, 不能解决 “数据极端情况下写丢失” 的问题.
  • 主从复制 + 哨兵不能提高数据的存储容量. 当我们需要存的数据接近或者超过机器的物理内存, 这样的结构就难以胜任了, 这时就需要下一章节所讲的 redis 集群来解决了.
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/408269
推荐阅读
相关标签
  

闽ICP备14008679号