架构设计|基于 raft-listener 实现实时同步的主备集群

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS AI 助手,专业版
简介: 本文介绍如何从数据库内核角度建立一套实时同步的主备集群,确保线上业务的高可用性和可靠性。本系统采用双 AZ 主备容灾机制,并要求数据与 schema 实时同步,同步时延平均在 1 秒内,p99 在 2 秒内。此外,系统支持高效的自动或手动主备切换,并能在切换过程中恢复丢失数据。

背景以及需求

  1. 线上业务对数据库可用性可靠性要求较高,要求需要有双 AZ 的主备容灾机制。
  2. 主备集群要求数据和 schema 信息实时同步,数据同步平均时延要求在 1s 之内,p99 要求在 2s 之内。
  3. 主备集群数据要求一致
  4. 要求能够在主集群故障时高效自动主备倒换或者手动主备倒换,主备倒换期间丢失的数据可找回。

为什么使用 Listener

Listener:这是一种特殊的 Raft 角色,并不参与投票,也不能用于多副本的数据一致性。

原本的 NebulaGraph 中的 Listener 是一个 Raft 监听器,它的作用是将数据异步写入外部的 Elasticsearch 集群,并在查询时去查找 ES 以实现全文索引的功能。

这里我们需要的是 Listener 的监听能力,用于快速同步数据到其他集群,并且是异步的执行,不影响主集群的正常读写。

这里我们需要定义两个新的 Listener 类型:

  1. Meta Listener:用于同步表结构以及其他元数据信息
  2. Storage Listener:用于同步 storaged 服务的数据

这样 storaged 服务和 metad 服务的 part leader 节点接受到写请求时,除了同步一份数据给 follower 节点,也会同步一份给各自的 listener 节点。

备集群如何接受数据?

现在我们面临几个问题:

  1. 两个新增 Listener 在接收到 leader 同步的日志后,应该如何再同步给备集群?
  2. 我们需要匹配和解析不同的数据操作,例如添加点、删除点、删除边、删除带索引的数据等等操作;
  3. 我们需要将解析到的不同操作的数据重新组装成一个请求发送给备集群的 storaged 服务和 metad 服务;

通过走读 nebula-storaged 的内核代码我们可以看到,无论是 metad 还是 storaged 的各种创建删除表结构以及各种类型数据的插入,最后都会序列化成一个 wal 的 log 发送给 follower 以及 listener 节点,最后存储在 RocksDB 中。

因此,我们的 listener 节点需要具备从 log 日志中解析并识别操作类型的能力,和封装成原请求的能力,因为我们需要将操作同步给备集群的 metad 以及 storaged 服务。

这里涉及到一个问题,主集群的 listener 需要如何感知备集群?备集群 metad 服务的信息以及 storaged 服务的信息?从架构设计上来看,两个集群之间应该有一个接口通道互相连接,但又不干涉,如果由 listener 节点直接发送请求给备集群的 nebula 进程,两个集群的边界就不是很明显了。所以这里我们再引入一个备集群的服务 listener 服务,它用于接收来自主集群的 listener 服务的请求,并将请求转发给自己集群的 metad 以及 storaged 服务。

这样做的好处。两边集群的服务模块是对称的,方便我们后面快速地做主备切换。

Listener 节点的管理和可靠性

为了保证双 AZ 环境的可靠性,很显然 Listener 节点也是需要多节点多活的,在 nebula 内核源码中是有对于 listener 的管理逻辑,但是比较简单,我们还需要设计一个 ListenerManager 实现以下几点能力:

  1. listener 节点注册以及删除命令
  2. listener 节点动态负载均衡(尽量每个 space 各个 part 分布的 listener 要均匀)
  3. listener 故障切换

节点注册管理以及负载均衡都比较简单好设计,比较重要的一点是故障切换应该怎么做?

listener 故障切换的设计

listener 节点故障切换的需求可以拆分为以下几个部分:

  1. listener 同步 wal 日志数据时周期性记录同步的进度(commitId && appendLogId);
  2. ListenerManager 感知到 listener 故障后,触发动态负载均衡机制,将故障 listener 的 part 分配给其他在运行的 listener;
  3. 分配到新 part 的 listener 们获取原先故障 listener 记录的同步进度,并以该进度为起始开始同步数据;

至于 listener 同步 wal 日志数据时周期性记录同步的进度应该记录到哪里?可以是存储到 metad 服务中,也可以存储到 storaged 服务对应的 part 中。

nebula 主备切换设计

在聊主备切换之前,我们还需要考虑一件事,那就是双 AZ 环境中,应该只能有主集群是可读可写的,而其他备集群应该是只读不能写。这样是为了保证两边数据的最终一致性,备集群的写入只能是由主集群的 listener 请求来写入的,而不能被 graphd 服务的请求写入。

所以我们需要对集群状态增加一种“只读模式”,在这种只读模式下,表明当前集群状态是处于备集群的状态,拒绝来自 graphd 服务的写操作。同样的,备集群的 listener 节点处在只读状态时,也只能接收来自主集群的请求并转发给备集群的进程,拒绝来自备集群的 wal 日志同步。

主备倒换发生时,需要有以下几个动作:

  1. 主集群的每个 listener 记录自己所负责的 part 的同步进度(commitId && appendLogId);
  2. 备集群的 nebula 服务转换为可写;
  3. 备集群的 listener 节点转换为可写,并且开始接收来自自己集群的 metad 和 storaged 进程的 wal 日志;
  4. 主集群的 listener 以及各个服务转换为只读状态,开始接收来自新的主集群的数据同步请求;

这几个动作细分下来,最主要的内容就是状态转换以及上下文信息保存和同步,原主集群需要保存自己主备切换前的上文信息(比如同步进度),新的主集群需要加载自己的数据同步起始进度(从当前最新的 commitLog 开始)

主备切换过程中的数据丢失问题

很明显,在上面的设计中,当主备切换发生时,会有一段时间的“双主”的阶段,在这个阶段内,原主集群的剩余日志已经不能再同步给备集群了,这就是会被丢失的数据。如何恢复这些被丢失的数据,可能的方案有很多,因为原主集群的同步进度是有记录的,有哪些数据还没同步完也是可以查询到的,所以可以手动或者自动去单独地同步那一段缺失数据。

当然这种方案也会引入新的问题,这段丢失地数据同步给主集群后,主集群会再次同步一遍回现在的备集群,一段 wal 数据的两次重复操作,不知道为引起什么其他的问题。

所以关于主备切换数据丢失的问题,我们还没有很好的处理方案,感兴趣的伙伴欢迎在评论区讨论。


感谢你的阅读 (///▽///)

对图数据库 NebulaGraph 感兴趣?欢迎前往 GitHub ✨ 查看源码:https://github.com/vesoft-inc/nebula

想和其他图技术爱好者一起交流心得?和 NebulaGraph 星云小姐姐 交个朋友再进个交流群;

相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
目录
相关文章
|
8月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
6月前
|
消息中间件 负载均衡 中间件
⚡ 构建真正的高性能即时通讯服务:基于 Netty 集群的架构设计与实现
本文介绍了如何基于 Netty 构建分布式即时通讯集群。随着用户量增长,单体架构面临性能瓶颈,文章对比了三种集群方案:Nginx 负载均衡、注册中心服务发现与基于 ZooKeeper 的消息路由架构。最终选择第三种方案,通过 ZooKeeper 实现服务注册发现与消息路由,并结合 RabbitMQ 支持跨服务器消息广播。文中还详细讲解了 ZooKeeper 搭建、Netty 集群改造、动态端口分配、服务注册、负载均衡及消息广播的实现,构建了一个高可用、可水平扩展的即时通讯系统。
779 0
|
4月前
|
存储 监控 NoSQL
Redis高可用架构全解析:从主从复制到集群方案
Redis高可用确保服务持续稳定,避免单点故障导致数据丢失或业务中断。通过主从复制实现数据冗余,哨兵模式支持自动故障转移,Cluster集群则提供分布式数据分片与水平扩展,三者层层递进,保障读写分离、容灾切换与大规模数据存储,构建高性能、高可靠的Redis架构体系。
|
分布式计算 Kubernetes Hadoop
大数据-82 Spark 集群模式启动、集群架构、集群管理器 Spark的HelloWorld + Hadoop + HDFS
大数据-82 Spark 集群模式启动、集群架构、集群管理器 Spark的HelloWorld + Hadoop + HDFS
556 6
|
9月前
|
负载均衡 算法 关系型数据库
大数据新视界--大数据大厂之MySQL数据库课程设计:MySQL集群架构负载均衡故障排除与解决方案
本文深入探讨 MySQL 集群架构负载均衡的常见故障及排除方法。涵盖请求分配不均、节点无法响应、负载均衡器故障等现象,介绍多种负载均衡算法及故障排除步骤,包括检查负载均衡器状态、调整算法、诊断修复节点故障等。还阐述了预防措施与确保系统稳定性的方法,如定期监控维护、备份恢复策略、团队协作与知识管理等。为确保 MySQL 数据库系统高可用性提供全面指导。
|
9月前
|
存储 NoSQL Redis
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 + 无锁架构 + EDA架构 + 异步日志 + 集群架构
阿里面试:Redis 为啥那么快?怎么实现的100W并发?说出了6大架构,面试官跪地: 纯内存 + 尖端结构 +  无锁架构 +  EDA架构  + 异步日志 + 集群架构
|
10月前
|
并行计算 PyTorch 算法框架/工具
融合AMD与NVIDIA GPU集群的MLOps:异构计算环境中的分布式训练架构实践
本文探讨了如何通过技术手段混合使用AMD与NVIDIA GPU集群以支持PyTorch分布式训练。面对CUDA与ROCm框架互操作性不足的问题,文章提出利用UCC和UCX等统一通信框架实现高效数据传输,并在异构Kubernetes集群中部署任务。通过解决轻度与强度异构环境下的挑战,如计算能力不平衡、内存容量差异及通信性能优化,文章展示了如何无需重构代码即可充分利用异构硬件资源。尽管存在RDMA验证不足、通信性能次优等局限性,但该方案为最大化GPU资源利用率、降低供应商锁定提供了可行路径。源代码已公开,供读者参考实践。
963 3
融合AMD与NVIDIA GPU集群的MLOps:异构计算环境中的分布式训练架构实践
|
10月前
|
存储 SQL 并行计算
【赵渝强老师】达梦数据库MPP集群的架构
达梦数据库提供大规模并行处理(MPP)架构,以低成本实现高性能并行计算,满足海量数据存储和复杂查询需求。DM MPP采用完全对等无共享体系,消除主节点瓶颈,通过多节点并行执行提升性能。其执行流程包括主EP生成计划、分发任务、各EP并行处理及结果汇总返回。为确保高可用性,建议结合数据守护部署。
413 0
|
人工智能 云计算 网络架构
阿里云引领智算集群网络架构的新一轮变革
11月8日~10日在江苏张家港召开的CCF ChinaNet(即中国网络大会)上,众多院士、教授和业界技术领袖齐聚一堂,畅谈网络未来的发展方向,聚焦智算集群网络的创新变革。
阿里云引领智算集群网络架构的新一轮变革
|
负载均衡 Dubbo 算法
集群容错架构设计
集群容错架构设计
160 1
集群容错架构设计