Redis 集群 容灾(同城多活)

目录 #

1. 背景 #

  • Redis 集群自身已经具备了高可用的特性,即使几个Redis节点异常或者挂掉,Redis 集群也会实现故障自动转移,对应用方来说也可以在很短时间内恢复故障。
  • 但是,如果发生了机房故障(断电、断网等极端情况),Redis集群节点全部挂掉(过半主节点挂掉),会造成集群服务不可用,对于核心业务来说是不可接受的。
  • 为了应对机房故障情况,保障在这种极端情况下,核心业务仍然可以正常访问 Redis 服务,本文将给出适合我司的 Redis 跨机房高可用解决方案。

2. 目标 #

  • 保障单机房整体故障时 Redis 缓存服务正常运行。
  • 单机房故障 RTO:30s,RPO:1s

3. 解决方案 #

3.1 核心能力 #

  • 客户端流量路由:支持按一定的策略,把流量分流到不同机房;机房故障后,流量自动流向其他机房。
  • 服务端故障转移:支持机房故障后,当前机房原来主节点的从节点,通过选举自动倒换成新的主节点。
  • 管理平台正常服务:任一机房故障,Renault 管理平台使用不受影响。

3.2 工作模式 #

组件在机房高可用场景下一般有多种工作模式,典型的有单集群模式及多集群模式。本节描述组件在各种工作模式下的部署方式及工作机制。

部署方案 方案说明
跨机房混合部署 将Redis集群主节点平均分配到各个机房,主从节点在不同机房
各机房独立部署集群 + 数据单向同步 各机房均独立部署集群,集群为热备模式,写请求均写入同一个集群,然后同步到其他集群
各机房独立部署集群 + 数据双向同步 每个机房部署一套Redis集群,同步核心业务写请求

数据同步方法:

数据同步方案 方案说明
客户端双写 客户端同时写入到各个集群
客户端代理 Netflix开源实现的Dynomite,通过代理层接受数据后写入各个需要同步的节点
改造难度大
客户端需要优化添加Dyno Client
服务端每个节点需要部署Dyno Node
read/write性能较原生差异较大,主要体现在write上,之间的差异随着node节点数越多越严重
伪从节点 基于Redis的Master-Slave复制协议,实现低延时、高可用的Redis多数据中心、跨公网数据复制
携程开源系统:https://github.com/ctripcorp/x-pipe
阿里RedisShake同步工具
写事件监听+MQ跨集群消息同步 读写在本机房,监听写事件 + MQ消息同步到其他机房
需要开启事件通知(PUB),修改Redis配置文件中的 notify-keyspace-events 配置(默认的redis并没有开启这个功能)
需要独立服务订阅写事件(SUB),并同步到其他集群
依赖MQ组件

不考虑客户端代理、发布订阅写事件

下面详细比较如下四种方案

  • 方案一:跨机房混合部署
  • 方案二:伪从节点+单向同步
  • 方案三:伪从节点+双向同步
  • 方案四:客户端双写+写监听服务+MQ消息队列

3.2.1 方案一:跨机房混合部署 #

3.2.1.1 部署方式 #

Redis集群主节点平均分配到各个机房,每个机房都有一个分片的副本;单个机房主节点数据占比不能过半。

[pic]

跨机房部署注意事项

  • 从节点选举需要过半主节点投票,因此不适合双机房部署,至少需要3机房
  • 业务请求访问响应时间会不稳定,同机房请求延迟在0.1ms,跨机房请求在1-3ms
3.2.1.2 工作机制 #
  • 流量路由

    • 默认读主节点(ReadFrom:Master)
    • 读从节点(ReadFrom: Replication)
    • 随机读主从(ReadFrom: Any)
    • 优先读本地机房(ReadFrom:LocalDC)— TODO:新增路由策略
  • 故障切换

    • 机房故障后,Redis 集群高可用机制,会将集群在30s内自动倒换并恢复正常访问
    • 同城机房间网络传输响应延迟2ms内,几乎不影响集群故障判定
    • Redis 集群故障转移后,客户端30s内自动刷新集群拓扑关系
  • 故障恢复

    • 机房恢复后,故障节点 Redis 会以从节点角色自动启动,并全量同步主节点数据(数据同步流量风暴 – 会在机房间路由器端口上限速控制)
  • 迁移方案

    • 服务核心业务 Redis 集群将会改造成跨三机房部署模式,不影响客户端正常使用
    • 业务根据实际情况,可将读请求路由策略修改为优先从本地机房读

Redis 故障恢复机制

  • 投票选主:只有持有槽的主节点才会处理故障选举消息,获得N/2+1以上选票的从节点将为新主。

  • 替换主节点:取消复制 → clusterDelSlot/clusterAddSlot把槽委派给自己 → 向集群广播通知变为主节点并接管了故障主节点的槽信息。

    • 全量同步过程
      • 从服务器连接主服务器,发送SYNC命令;
      • 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件,并使用缓冲区记录此后执行的所有写命令;
      • 主服务器BGSAVE执行完后,向所有从服务器发送RDB文件;
      • 从服务器收到快照文件后丢弃所有旧数据,载入收到的RDB快照;
      • 主服务器快照发送完毕后,开始向从服务器发送缓冲区中的写命令;
      • 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令。
    • 全量同步数据评估,假如100GRedis集群,三机房,一主二从
      • 根据Renault公共集群统计,8G使用容量的Redis,生成RDB文件大小约3G左右,生成时间75s
      • RDB传输,单一机房故障恢复时,将有10台Redis执行主从同步,约有30G流量从其他两个机房流入。
      • RDB加载,3G RDB数据加载时间约90s

3.2.2 方案二:伪从节点+单向同步 #

3.2.2.1 部署方式 #

双机房部署,业务读写默认集群,开发部署 Renault 复制服务(RRS, Renault Replicate Service),伪装从节点实时将默认集群数据同步到备用集群。

[pic]

3.2.2.2 工作机制 #
  • 流量路由

    • 双机房独立部署,均访问默认集群(热),故障后访问备用集群(冷)
  • 故障切换

    • 默认集群故障时,SDK熔断默认集群请求,并将流量切换到备用集群
  • 故障恢复

    • 机房故障恢复后,原默认集群将被reset,RRS 服务将反向同步
  • 迁移方案

    • 服务核心业务 Redis 集群将会搭建备用集群,并实时同步默认集群数据
    • 业务客户端需要升级 SDK,支持自动故障切换功能

DC2机房业务客户端自动故障切换:

[pic]

故障恢复后(反向同步):

[pic]

3.2.3 方案三:伪从节点+双向同步 #

3.2.3.1 部署方式 #

双机房独立部署,均访问本地集群;开发部署 Renault 复制服务(RRS, Renault Replicate Service),伪装从节点实时双向同步。

[pic]

3.2.3.2 工作机制 #
  • 流量路由

    • 双机房独立部署,业务优先访问本地机房集群(TODO:需要开发一种集群选择策略)
    • RRS 服务双向同步数据(需要解决双向同步成环问题)
  • 故障切换

    • DC1机房集群故障时候,上层流量自动切换到DC2机房,Redis层不需要处理
  • 故障恢复

    • DC1机房集群故障恢复,首先通过RRS服务从DC2全量同步数据,之后增量同步
  • 迁移方案

    • 服务核心业务 Redis 集群将会搭建双活集群,并通过 RRS 服务实时双向同步
    • 业务客户端需要升级 SDK,支持启动时选择哪个集群

数据库层面的多活,双向同步存在以下困难:

  • 两边都改了如何解决冲突?

    • 跨机房双写无法保证缓存的一致性,需要应用侧可以容忍对应的缓存不一致场景
  • RRS复制中断或者故障如何处理?

  • 数据同步如何防环?

    • 方法1:通常需要应用方配置使用,按业务类型分流(不能为通用解决方案)
    • 方法2:数据层面添加字段标识数据源(带来一定开销,并且对于数值计算类数据不能添加标识)
    • 方法3:x-pipe - 定制Redis,在内容分发上做处理,服务端能够识别不同的链接类型,在同步数据之初便加以控制在内容分发上做处理,服务端能够识别不同的链接类型,从而做到有的放矢,在同步数据之初便加以控制

3.2.4 方案四:客户端双写+写监听服务+MQ消息队列 #

3.2.4.1 部署方式 #

双机房/三机房每个独立部署,应用客户端均访问本地集群;借助MQ通过异步双写机制双写同步到其他集群。

[pic]

3.2.4.2 工作机制 #
  • 流量路由

    • 多机房独立部署,业务优先访问本地机房集群(同方案三)
    • MQ中间件+写事件监听服务做双写同步
  • 故障切换

    • DC1机房集群故障时候,上层流量自动切换到DC2机房,Redis层不需要处理(同方案三)
  • 故障恢复

    • DC1机房集群故障恢复,首先通过RRS服务从DC2全量同步数据,之后增量同步(同方案三)
  • 迁移方案

    • 服务核心业务 Redis 集群将会搭建多活集群,并通过 MQ 发布订阅方式实现多集群间双向同步
    • 业务客户端需要升级 SDK,支持启动时选择哪个集群

双机房/三机房每个独立部署,多活部署主要问题:

  • 两边都改了如何解决冲突?

  • 跨机房双写无法保证缓存的一致性,需要应用侧可以容忍对应的缓存不一致场景,应用如果依赖缓存强一致性,则不合适该方案。

    • DC1和DC2两边都写了同一个Key,最终互相覆盖
  • 如何保证消息的顺序?如何保证消息成功发送及消费?

3.3 成本比较 #

部署成本 改造成本 使用成本
方案一:跨机房混合部署 成本-低
适用于三机房及以上
2副本,增加50%容量
改造成本-低
只用按照要求部署集群
SDK路由-优先读本地机房
性能有下降
本机房访问不受影响,跨机房访问延迟1-3ms
方案二:伪从节点+单向同步 成本-中
新增备用集群,增加100%容量
部署同步服务
改造成本-中
需要开发集群间数据同步服务(已有待完善)
读写分离
性能有下降
本机房访问不受影响,跨机房访问延迟1-3ms
读可优化成读本地机房,性能不受影响
方案三:伪从节点+双向同步 成本-中
各机房均新增集群,双机房增加100%容量,三机房增加200%
容量部署同步服务
改造成本-高
双向复制成环很难解决,需要定制Redis
两边都写,冲突问题难解决
需要开发集群间数据同步服务(已有待完善)
性能不受影响
读写本地机房
方案四:客户端双写+写监听+MQ消息同步 成本-高
各机房均新增集群,双机房增加100%容量,三机房增加200%容量
部署写监听服务
部署MQ消息集群
改造成本-中
依赖MQ中间件
性能稍有下降
同步写跨机房访问,性能会严重下降
异步写性能稍有下降
应用能容忍两边不一致场景

方案选定:

  • 目前同城多活选用方案一(跨机房混合部署
  • 未来异地多活再考虑方案三和四

3.4 场景建议 #

方案一适应场景

  • 适用于三机房及以上【两机房故障时候无法成功选举】
  • 要能接受写缓存时间在1-2ms内【不可避免跨集群写数据,目前sh1读写tx1时延约1.3ms,sh1读写sh1时延约0.2ms】
  • 配置优先读本地机房的话,能接收读数据时延【主从数据同步毫秒级时延】

其他场景建议

  • 不接受写缓存慢场景、不接受读从节点数据延迟场景

    • 服务端:每个机房都要独立部署Redis集群,并且双向同步数,推荐方案三
    • SDK改造:需要支持优先读写本地Redis集群路由策略
    • 业务端:升级SDK版本

4. 里程碑 #

描述组件为了达成机房高可用需要做的事情,包括事项、优先级、预期完成时间、负责人等信息。

对于中间件,一般有如下重要时间点:

  • 服务端机房高可用方案设计完成
  • 服务端机房高可用方案在测试集群验证通过
  • 客户端机房高可用方案设计开发完成
  • 业务集群机房高可用方案验证:明确 RPO 和 RTO

方案一跨机房混合部署改造事项:

  • 管理平台:

    • 搭建核心集群
      • 机器添加机房标识 (Done),
      • 机架标识,是否属于同一机架,
      • 机房高可用校验 (Done)
        • 主从节点数至少3个,且分布在不同机房
        • 单机房主节点数不能过半
      • 集群调整,确保核心集群Redis实例机房按大集群要求部署,
    • 核心应用识别及迁移
      • 核心缓存标识 - 源于用户?核心应用标识?
      • 新增核心缓存将绑定到核心集群,非核心缓存绑定到公共集群
      • 缓存客户端监控(CAT QPS)集成到管理平台 - 便于缓存迁移时,从技术角度判定是否需要迁移
      • 核心缓存迁移
        • 已迁移(arch-100%,cl-100%)
        • 待迁移(md、mkt、yw)(需要推动升级客户端并迁移)
  • 客户端:

    • 读写分离,优先读本机房实例 ,
      • 熟悉Lettuce 读写分离相关源码,确定可行方案(方案可行 - 选择NEAREST或者自定义ReadFrom)
      • 自定义ReadFrom,优先从本机房读,本机房无实例则从Master节点读 (done)
        • 用户在 Apollo 配置读取数据方式(Master/Slaver/Any/Nearest/LocalZone)
  • Redis集群:

    • 故障演练,
      • repl-timeout 60s适当大小;
      • 完整操作机房Redis实例下线、Redis实例恢复操作
    • 缓存恢复时的数据同步抑制方法, (Done,需要时在交换机端口上限速)
      • 机房故障后,强制下线故障机房Redis实例
      • 机房恢复后,重新部署Redis实例,逐一添加到集群,并调整合理主从关系
  • ZK & Apollo ,

    • 切换演练
    • 在线客户端列表对比(老zk的会清零,新zk临时节点数目和原来的一致)
    • 将线上ZK从zk1切换到zk3

2021-11-30 Redis 集群机房高可用方案整体设计完成

2021-12-31 客户端路由开发、跨集群测试验证通过

2022-2-28 推广接入试点,及真实数据故障演练

2022-3-31 业务集群机房高可用方案验证

5. 相关文档 #

6. 参考资料 #