NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

一文读懂 GaussDB(for Mongo) 的计算存储分离架构

  • 2020-08-17
  • 本文字数:2722 字

    阅读完需:约 9 分钟

一文读懂GaussDB(for Mongo)的计算存储分离架构

1.摘要

GaussDB(for Mongo)是华为云自主研发兼容 MongoDB4.0 接口的文档数据库。基于共享存储的存算分离架构,对于传统 MongoDB 社区版有如下优势:


  • 秒级添加 Secondary 节点(相比社区版 Mongo 小时级添加 Secondary 节点)

  • 基于 WAL 复制, Secondary 节点无写 IO,从根本上解决社区版 Seconary 节点 Oplog 脱节问题

  • Primary/Seconary 无任何 IO 交互,Secondary 节点个数理论无上限, 支持百万 OPS 的读事务能力

  • LSMTree Compaction 计算/IO 卸载到 Compaction 统一调度池,集中管理,不浪费用户读写 IO

  • 基于共享存储,Chunk 分裂/迁移动作不引起真实 IO,只更新路由元数据,秒级分裂/均衡

2.GaussDB(for Mongo)技术架构

1)容忍更多 Shard 宕机


与社区版 MongoDB 的Share-Nothing模式不同的是,GaussDB(for Mongo)采用Share-Storage架构,计算存储分离。集群模式下,N 个 Shard 节点,可以容忍 N-1 个 Shard 宕机。



某个 Shard 节点宕机后,其负责的数据由于存在于共享的存储池中,因此不需要物理拷贝数据,只需要修改元数据路由信息,即可被其他分片节点接管。


2)更快的分裂与均衡能力


此外,由于 Chunk 数据在存储池中,Chunk 的分裂与均衡不涉及到数据拷贝,可以做到分钟级分裂与扩容,分裂与扩容对用户的影响也远比社区版 MongoDB 小。



3)百万级读 OPS 能力


GaussDB(for Mongo)副本集模式下,Primary/Secondary 节点之间共享同一份数据库文件。Secondary 节点只复制 Primary 节点的 WriteAheadLog 以及 LSMTree 的结构变更信息,并应用到内存中。Secondary 节点没有 LSMTree 的 Compaction 和 Flush 任务,因此对用户的读业务影响很小。



此外,由于Share-Storage的架构优势,添加 Secondary 节点并不需要拷贝数据,添加 Secondary 节点的动作可以秒级完成。而 Primary/Secondary 之间只传递元数据变更,不传递 WriteAheadLog,因此 Secondary 节点的个数即使变多,也不影响 Primary 节点的写性能。Secondary 节点可以水平扩展,支撑百万级的读 OPS。


4)主节点 IO 卸载


LSMTree 的写压力来源于三部分:


  • 用户的业务写入导致的 Memtable Flush

  • 后台 SST 文件 Compaction

  • WAL 的持续写入


根据线上业务的实际测算,三者的 IO 资源消耗占比为: 1:10:1。后台的 SST 文件 Compaction 占了绝大部分 IO 带宽,通过将 Compaction 任务集中化管理,从计算池卸载到存储池,进一步减少了用户计算节点的 CPU 和 IO 资源消耗。



5)GaussDB(for Mongo) 只读节点设计


  • 传统社区版 MongoDB 副本集基于 Oplog 做数据复制,只读节点需要镜像主节点的所有写 IO 操作。GaussDB(for Mongo) 的只读节点和主节点共享同一份底层数据库文件(LSMTree 的 SST 文件),只读节点并不自己生成 SST 文件。

  • 随着业务数据的写入,Compaction 的不断执行,LSMTree 的当前版本(包含哪些 SST 文件)不断更新,LSMTree 的元数据更新(增删 SST 文件的记录)被同步到只读节点执行。

  • RocksDB 中,数据的变更被持久化到 WAL 里,元数据的变更(增删文件的操作, 叫做 VersionEdit)被持久化到 Mainifest 里。RocksDB 的数据和元数据是分开的,WAL 流和 VersionEdit 流是并行的,没有严格的先后顺序。为了保证只读节点和主节点完全一致的事件回放顺序,WAL 和 VersionEdit 流必须要合并成一个流,在双流合并后,通过 LSN 就可以为每个事件(WAL 的写操作/VersionEdit)定序。



  • 基于 WAL+VersionEdit 复制,而不基于 Oplog 复制

  • 共享文件(sst/wal)的生命周期管理由主节点负责 sst 文件和 wal 的文件的生命周期由主节点负责。RocksDB 中,SST 文件通过层级的引用计数来维持不被删除。如下图,RocksDB 的每个游标会维持 SuperVersion,如下图中的 S0,S1,S2。每个 SuperVersion 会引用一个 Version,一个 Version 代表 LSMTree 在不断变形(通过增删 SST 文件变形)的过程中,某个时间点的形状,最新的 Version 就代表 LSMTree 当前的形状。



  • 在 GaussDB(for Mongo)中,主节点会记录所有只读节点在使用的 Version,并为这些 Version 增加引用计数从而维持 SST 文件的生命周期。对于 WAL,主节点会记录所有只读节点中最老的 LSN(oldestLsn),最老的 LSN 来自于复制最慢的只读节点。并删除比 oldestLsn 还旧的 WAL 文件。

  • 元数据变更通知,无论是 oldestLsn 还是只读节点的当前在用的活跃的 Version,都需要及时推进,这些元数据的变更是通过主从节点的定期心跳上报到主节点上的。主节点利用心跳数据对垃圾版本与 WAL 做清理。如下图所示,在经历一次心跳后,主节点发现 Secondary0 的 Version0 和 Secondary1 的 Version0 不再使用。删除这两个 Version 后,SST0 的引用计数为 0,表示 SST0 可以被删除。OldestLsn 也从 100 推进到了 250,可以清理掉 250 之前的 WAL。



  • 只读节点的 memtable 的释放:主节点的 Memtable 不会实时 Flush 为 SST 文件。如果只读节点不处理主节点的 Memtable 的话,只读节点的数据就不是实时的,且存在数据一致性问题。只读节点通过回放 WAL 到内存的 Memtable 中,来覆盖 SST 文件与主节点的 Memtable 的 Gap。上文介绍了只读节点是不往共享存储写入数据的, 所以只读节点上的 Memtable 最后的结局一定是被丢弃掉。但什么时候丢弃这个 Memtable 就是一个问题。过早的丢弃,会造成 SST 文件与 Memtable 之间的数据不连续,存在 Gap,过晚的丢弃会造成内存的浪费。只有当只读节点识别到 SST 的数据已经完全能够 Cover 某个 Memtable 时,这个 Memtable 才可以被丢弃。

  • GaussDB(for Mongo)的只读节点在每次应用 VersionEdit 后,检查所有 SST 中的最大的 LSN 与 Memtable 的最小的 LSN 的关系,来决定是否要丢弃某个 Memtable。

  • 内存元数据的反向更新:传统的复制,数据流从 Oplog 来,走一遍完整的数据库 Server 层 CRUD 接口,再落到引擎层。这种逻辑和主节点上业务的写入逻辑是一致的,因此 Server 层的一些内存元数据结构,在这个过程中就自然而然的得到更新了。但是当采用基于 WAL 的复制后,整个 WritePath 并不经过只读节点的 Server 层。因此 Server 层的内存元数据更新,就是一个很大的挑战。在这里,只读节点对每一条 WAL 做分析,如果 WAL 的内容会影响 Mongo 内存元数据,就会 reload 对应的元数据模块。


3. 总结

GaussDB(for Mongo) 基于 Share-Storage 架构,实现秒级 Chunk 分裂与均衡,对业务影响更小,水平扩展速度更快,能容忍更多节点宕机。只读节点功能,实现了一份数据多计算节点共用的功能。极大的提升了存储的利用效率,提高了计算节点的读取数据能力。为了让副本节点具有持续的读扩展能力,整个只读方案采用元数据的同步模式,在不降低主节点负载的情况下,极大的提升了整个系统的读数据的处理能力。为 3 节点,5 节点,乃至于 15 节点以上的副本集的工作提供了可能。


2020-08-17 10:141872

评论 2 条评论

发布
用户头像
rocksdb 的WAL应该是逻辑日志,不像mysql 的redo log这种物理日志,就无法做到在存储节点层直接进行recovery 吧
2021-02-01 17:21
回复
用户头像
但是在图3里有4条从计算节点到存储节点的path,oplog, wal, data都下沉? 这还是 log is database 吗?

基于 WAL+VersionEdit 复制,而不基于 Oplog 复制

2021-02-01 17:18
回复
没有更多了
发现更多内容

1.0 Go语言从入门到精通:Go语言介绍

xcbeyond

28天写作 Go 语言

软件架构-事件驱动架构

看山

架构 事件驱动架构

架构师训练营第七周作业 - 学习总结

阿德儿

一种C++中支持界面调用函数的实现方法

长不胖的Garfield

浅谈EMC电磁兼容设计—概念篇

不脱发的程序猿

28天写作 二月春节不断更 电路设计 EMC 电磁兼容

使用 Tye 辅助开发 k8s 应用竟如此简单(四)

newbe36524

.net Docker Kubernetes .net core dotnet

【LeetCode】绝对差不超过限制的最长连续子数组Java题解

Albert

算法 LeetCode 28天写作 2月春节不断更

Eureka 部分机制记录

常清静

Spring Cloud 原理 Eureka

LeetCode 第 46 场双周赛题解

宫水三叶的刷题日记

面试 LeetCode 数据结构与算法

堆栈与队列学习总结

Nick

数据结构 算法 堆栈 队列

管理笔记【10】十二条给管理者的人事管理经验

L3C老司机

28天写作

甲方日常 92

句子

工作 随笔杂谈 日常

LeetCode题解:198. 打家劫舍,动态规划(缓存偷盗状态),JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

dubbo源码v2.7分析:结构、container入口及线程模型

程序员架构进阶

微服务 七日更 28天写作 2月春节不断更 dubbo源码

第7周课后练习-性能优化一

潘涛

架构师训练营 4 期

28天瞎写的第二百四十天:我与正念的故事

树上

冥想 28天写作 正念 焦虑 平静

十二周作业&总结

胡益

canvas从入门到猪头

执鸢者

大前端 canvas

语音聊天室申请上麦的用例文档

郭郭

第13周学习总结

Binary

这些面试题你会吗?双非本科字节跳动Android面试题分享,大厂内部资料

欢喜学安卓

android 程序员 面试 移动开发

架构师训练营第七周作业 - 命题作业

阿德儿

关于星座的趣谈「Day 2」

道伟

28天写作

【vue2 & G6】快速上手

德育处主任

大前端 可视化 数据可视化 G6 antv/g6

设计模式简介

happlyfox

设计模式 28天写作

诊所数字化:私域运营的本质

boshi

数字化转型 医疗 私域运营 七日更 28天写作

实战 LeetCode 15.三数之和、18.四数之和,并扩展至 N 数之和

与你一起学算法

架构师训练营 4 期 第8周

引花眠

架构师训练营 4 期

第十三周课后练习

Binary

这些面试题你会吗?月薪20k+的Android面试都问些什么?面试必问

欢喜学安卓

android 程序员 面试 移动开发

SICP 习题解答 1.6

十元

一文读懂GaussDB(for Mongo)的计算存储分离架构_数据库_华为云与计算_InfoQ精选文章