写点什么

一文读懂 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:142181

评论 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
回复
没有更多了
发现更多内容

ARTS-1

你当像鸟飞往你的山

ARTS 打卡计划

那些会阻碍程序员成长的细节[2]

MavenTalker

程序员 程序人生

5G时代下应用的安全防御研究

Nick

5G 5G网络安全 5G安全

DDD 中的那些模式 — 使用 Specification 管理业务规则

Joshua

设计模式 领域驱动设计 DDD 架构模式

clang-format 使用与集成介绍

Geek_101627

后疫情时代,区块链的发展迎来曙光!

CECBC

CECBC 区块链技术

我的编程之路 -6(新时代)

顿晓

android 编程之路 时代

MySQL 可重复读,差点就我背上了一个 P0 事故!

楼下小黑哥

Java MySQL

爬虫框架Scrapy应用实践-淘宝保险频道数据抓取【2】-抓包分析

hadesxiong

Python 爬虫 保险 Scrapy

Apache DolphinScheduler新特性与Roadmap路线

代立冬

大数据 数据中台 工作流调度 海豚调度 数据湖调度

区块链技术大显身手,仅用20分钟就打完一场官司!

CECBC

CECBC 区块链技术 数字版权 存证

ARTS打卡第一周

GKNick

Mysql索引不会怎么办?6000字长文教会你

Super~琪琪

MySQL 数据库 sql 索引

眼中有码,心中无码

小眼睛聊技术

学习 深度思考 程序员 最佳实践 算法

数据产品经理实战-数据门户搭建(上)

第519区

数据中台 开发数据

ARTS 打卡 WEEK2

编程之心

ARTS 打卡计划

如何使用 Apache CXF 快速实现一个 WebService

Rayjun

Java WebService CXF

体验一次简洁的代码

你当像鸟飞往你的山

深入计算机底层,从几本靠谱的书开始

HackMSF

计算机工作原理

像孩子一样认识新事物 —— 读《终身幼儿园》

YoungZY

学习 读书笔记 读书

如何做好Code Review?

架构精进之路

Code Review

Java日志门面系统

泛泛之辈

Java 日志 slf4j

如何设置线程池参数?美团给出了一个让面试官虎躯一震的回答。

why技术

Java 源码分析 面试 线程池

重学 Java 设计模式:实战单例模式

小傅哥

设计模式 编程思维 重构 优化代码

Flutter开发环境配置

玉龙BB

flutter android vscode

[ARTS打卡] week 01

Mau

ARTS 打卡计划

2万字长文带你细细盘点五种负载均衡策略。

why技术

Java 负载均衡 源码分析 面试 dubbo

ARTS week 3

刘昱

关爱孩子的心理建设

Neco.W

人生 感悟 教育

ARTS打卡计划_第一周

叫不醒装睡的人

ARTS 打卡计划

ARTS-01

NIMO

ARTS 打卡计划 ARTS活动

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