OceanBase 的写盘与传统数据库有什么不同?

news2024/11/26 10:18:58

背景

在数据库开发过程中,“写盘”是一项核心操作,即将内存中暂存的数据安全地转储到磁盘上。在诸如MySQL这样的传统数据库管理系统中,写盘主要有以下几步:首先将数据写入缓存池;其次,为了确保数据的完整性,系统会将数据写到"二进制日志文件"中,这一步骤对于在系统崩溃时恢复数据至关重要;最后将数据写入磁盘。为了提升这一过程的性能,可以使用SSD盘、延迟写或增加缓存池大小等多种策略。

而在OceanBase 数据库中,写盘与传统数据库大不相同。由于OceanBase 存储引擎是基于LSM-Tree架构实现的,数据被分为了静态基线数据(放在 SSTable 中,存储于磁盘)和动态增量数据(放在 MemTable 中,存储于内存)两部分。当内存的增量数据达到一定规模的时候,会触发冻结转储操作,保证将要被转储的MemTable不再进行新的数据写入,同时生成新的活动MemTable(冻结,Freeze),以及将被冻结MemTable的数据存储到磁盘上以释放内存空间(转储,Mini Merge)。此时,一些小伙伴会存在疑问:到底如何读取那些所谓的"row"结构,才能将MemTable的数据存储到磁盘上?

本文从MemTable的结构、读行过程讲起,逐步带大家解开疑问,并对OceanBase 的MemTable产生新的认识。

MemTable的结构

OceanBase中的MemTable是由BTree和Hash Table两个数据结构结合而成,二者各司其职,BTree发挥其范围查询的能力,Hash Table发挥其点查的能力。什么意思呢?我们来看下面这张图。图中是BTree和Hash Table的一个结合结构,BTree的每个叶节点和Hash Table的节点都指向着某一个row节点,而row节点指向着一个链表(实现上是双向链表)。我们只需要知道,每个rowkey在MemTable中最终是对应着一个row节点(即一行数据),这行数据可能经过了insert、update、delete、insert的一系列操作,在OceanBase中,每个这样的操作都会通过一个MvccNode存储起来,并按照操作的先后顺序从尾到头组成一个MvccNode list,链表尾部是最新的操作。

好,到这里我们已经掌握了OceanBase中MemTable的"核心科技"了,也没那么复杂是不是?

接下来,我们一起经历一遍冻结后的MemTable在将要存储到磁盘时,逐行读取数据的过程,尽可能简单地加深我们对"核心科技"的理解。

Memtable迭代读行过程

为了方便大家自主阅读代码,后续介绍过程会贴出部分源码,所有源码都基于GitHub master分支

容易理解的是,逐行读取数据是一个scan的过程,因此我们会通过遍历BTree而不是HashTable来完成读取。

整个迭代过程可以分为两个部分:一部分是对BTree的遍历,每次我们将得到一个rowkey对应的行,换句话说就是得到一个行的MvccNode list;另一部分则是对MvccNode list的遍历,过程中得到一个行的多版本数据。

那么,什么叫“行的多版本数据”呢?既然说到这,就不得不插入一段对MvccNode的解释。

我们刚才提到,每个MvccNode代表着一个操作,这个操作放到事务中来说的话,其事务的状态可能是已提交的,也可能是未提交的,就是图中的status(commit/uncommit)。MvccNode会记录事务的提交版本号,即图中的trans_version,对于未提交的操作来说,trans_version是MAX。

由于每个rowkey不允许多写,容易理解MvccNode list中的uncommit node一定是属于同一个事务的(即事务id相同),并且一定处在MvccNode list的最新位置,即链尾。

MvccNode的具体实现结构如下所示:

struct ObMvccTransNode {
  transaction::ObTransID tx_id_;  // 事务id
  share::SCN trans_version_; // 事务提交版本
  share::SCN scn_; // 数据所在日志的 scn
  int64_t seq_no_;  // 在事务中的sql numble // 可能一个存储过程有多个相同sql no的mvcc node
  share::SCN tx_end_scn_; // 数据所在事务的 commit/abort 日志的 scn // 未提交为max
  ...
  uint8_t type_;  // 标记是否是COMPACT
  uint8_t flag_;   // 事务状态 (F_COMMITTED/F_ABORTED 等等
};
需要注意的是,每个操作实际上是被包含在一个事务中的,因此该操作存在一个sql_no_,标记着其在整个事务中的序号,同时操作本身对应着一条日志,scn_记录了该日志的scn(简单理解为时间戳即可),而整个事务的提交对应着另一条日志,tx_end_scn_记录了该日志的scn。trans_version_记录着事务的提交版本,我们只需要知道这是和日志时间戳不同的两个维度的概念。

我们已经知道了uncommit和commit的概念,那么图中被标记为compact的node是什么呢?

简单来说,compact node是将后续所有已提交MvccNode中数据进行整合后得到的一个特殊的包含完整行的node。

如下图所示,三个MvccNode从尾到头分别是update/update/insert操作,其中insert node是包含全部列数据的完整行,update node只包含更新列的数据的部分行。对于一个想要全部列的查询来说(这里我们忽略版本的问题),每次都需要读取这三个node并整合成一个新的完整行。因此,compact node产生了。我们会将整合三个node所得到的完整行保留下来,最终形成一个包含当前最新完整行的compact node。

再回到最开始的问题:"什么叫行的多版本数据?"

在逐行读取的过程中,对于每个rowkey,我们最终会读取出多个sql numble不一样的未提交行(如果有的话)、一个compact行以及多个多版本行,这就是所谓的“行的多版本数据”。

这里就涉及一个版本范围的限制,即(base_version, snapshot_version, multi_version_start) 三元组。我们的读取实际上附带着这样一个版本范围。我们所读取出的compact行是在遍历MvccNode list过程中,将多个提交版本在(base_version, snapshot_version)之间的compact node或者commit node再次整合所得。而读取出的多个多版本行分为两部分,一部分是将事务提交版本在(multi_version_start,snapshot_version)之间的node进行整合所得,可能会有多个结果,每个结果是由相同事务提交版本的node整合所得;另一部分是将事务提交版本在(base_version,multi_version_start)之间所有node整合所得,只有一个结果。

看完以上解释,大家其实已经掌握了七八了,整个逐行读取的过程就是根据所谓的版本范围,遍历BTree上的每个rowkey,再将每个rowkey所拥有的MvccNode通过一系列的整合操作,得到一条条提交或者未提交的行记录,最后将这些行依次写入微块、微块写入宏块、多个宏块形成SSTable。

如果对源码细节不感兴趣的同学,看到这已经可以转身离开了。但是剩下的同学别着急,让我们继续向细节探寻。

整个逐行读取过程通过ObMemtableMultiVersionScanIterator实现,参见ObMemtableMultiVersionScanIterator::inner_get_next_row。其中包含两个子迭代器ObMultiVersionRowIteratorObMultiVersionValueIterator,两者分别用于迭代BTree和迭代MvccNode list:

class ObMultiVersionRowIterator {
  ...
  common::ObVersionRange version_range_; // (base_version,snapshot_version,multi_version_start)三元组
  ObMultiVersionValueIterator value_iter_;
  ...
  ObIQueryEngineIterator *query_engine_iter_;  // BTree迭代器
};
class ObMultiVersionValueIterator {
  ...
  common::ObVersionRange version_range_;
  ObMvccRow *value_;  // 装载着ObMvccTransNode *list_head_
  ObMvccTransNode *version_iter_; // value->list_head_
  ObMvccTransNode *multi_version_iter_; // 用于迭代多版本行,来自某一次的version_iter_
  int64_t max_committed_trans_version_; // 这一行的最大已提交版本
  share::SCN cur_trans_version_;
  bool is_node_compacted_;
  bool has_multi_commit_trans_;
  share::SCN merge_scn_; // 来自merge_param.scn_range_.end_scn_
};

迭代过程被实现为一个简易的状态机,每个状态以及每个状态的行为如下:

状态行为输出行结果
SCAN_END通过ObMultiVersionRowIterator row_iter_迭代每个rowkey,得到下一个rowkey的MvccNode list,存放到ObMultiVersionValueIterator value_iter_.version_iter_。
SCAN_UNCOMMITTED_ROW通过value_iter_的version_iter_迭代MvccNode list,每次先拿出一个uncommit node,然后向后迭代将所有相同sql_no_的uncommit node compact成一行,将该行输出。该状态会重复进入直到迭代到commit node。多个uncommit行,每行由多个sql_no_相同的node compact而成
SCAN_COMPACT_ROW通过value_iter_的version_iter_继续迭代MvccNode list,将所有访问到的commit或者compact node compact成一行,将该行输出。在遇到compact node或者事务提交版本小于等于base_version的node时结束遍历,并将value_iter_置为空。一个事务提交版本在(base_version, snapshot_version)范围内所有commit node的compact行
SCAN_MULTI_VERSION_ROW通过value_iter_的multi_version_iter_迭代MvccNode list,将所有访问到的具有相同trans_version并且trans_version大于multi_version_start的node compact成一行,将该行输出。所有trans_version介于(base_version,multi_version_start)之间的node将compact成一行输出。特别地,当遇到事务提交版本大于multi_version_start的compact node时,后续相同事务提交版本的node可以直接跳过,因为已经通过compact node进行过compact了。多个多版本行,在(snapshot_version,multi_version_start)范围内每个相同事务提交版本的node compact为一个多版本行,在(multi_version_start,base_version)范围内,所有事务提交版本的node compact为一个多版本行

状态之间的转换过程如下:

需要注意的是,在SCAN_UNCOMMITTED_ROW向SCAN_COMPACT_ROW进行状态转换时会对multi_version_iter_进行初始化:
当前version_iter_是commit node,其trans_version为所有commit node中的最大已提交版本,如果该版本比multi_version_start_小,或者后续node没有比最大已提交版本小的trans_version,multi_version_iter_被初始化为null,否则初始化为当前version_iter_

以上迭代读行细节的描述,可能会有一定的门槛,如果现在没有理解也没有关系。如果大家真的对源码本身感兴趣并且做了一定的研究之后,回过头来看这些内容,相信会有更深入的思考。

后记

这篇博客依然省略了很多细节,旨在分享一个以存储视角对MemTable最基本的解读,希望能够让大家对OceanBase相关源码产生兴趣(可以在Github上找到),只有真正的去探究了源码,才能领略到其中的艰涩与风光。当然,笔者文中难免有不少纰漏,也欢迎大家指正和讨论,错误的订正也是加深理解的一个过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2221053.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Qt】控件——Qt输入类控件、常见的输入类控件、输入类控件的使用、Line Edit、Text Edit、Combo Box、Spin Box

文章目录 Qt5. Qt显示类控件Line EditText EditCombo BoxSpin BoxQDateTimeEditDialSlider Qt 5. Qt显示类控件 Line Edit QLineEdit 用于表示单行输入框。可以输入一段文本,但是不能换行。 属性说明text输入框中的文本inputMask输入内容格式约束maxLength最大长度…

【HAD】Half-Truth: A Partially Fake Audio Detection Dataset

文章目录 Half-Truth: A Partially Fake Audio Detection Dataset背景key points研究数据集设计评价指标实验基线:utterance-level分类(话语级)基线:segment-level分类(片段级)Half-Truth: A Partially Fake Audio Detection Dataset 会议/期刊:Interspeech 2021 CCF-C…

哈佛医学生一个月吃720个鸡蛋,结果发现......

你们听说过吃鸡蛋还能降低胆固醇的操作吗? 前段时间,哈佛医学院的学生尼克诺尔维茨(Nick Norwitz)就做了这样一个实验,得出的结果让大家震惊! 许多人认为狂吃鸡蛋不会有好处,因为这会摄入大量胆…

FFT过程中自动补零,补零部分FFT结果不为零

在 FFT(快速傅里叶变换) 中,补零(Zero Padding)是为了使信号的点数符合 2 的幂次方,以提高 FFT 的计算效率。然而,即使你对信号进行了补零,FFT 计算后在补零部分可能会得到复数结果不…

<项目代码>YOLOv8工具识别<目标检测>

YOLOv8是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv8具有更高的…

数字化营销助企业在生态平台实现内卷突围

在当今数字化时代,企业竞争激烈,内卷化严重。而数字化生态平台建设与数字化营销为企业带来了新机遇。 数字化生态平台意义重大。它能整合企业内外资源,提高运营效率。打破地域限制,拓展市场,吸引更多客户。还能为企业创…

TCP——Socket

应用进程只借助Socket API发和收但是不关心他是怎么进行传和收的 数据结构 图示Socket连接 捆绑属于隐式捆绑

如何快速解决谷歌网站页面收录难题?

在外贸网站的运营中,页面无法被谷歌收录是一个常见的困扰。即便你的内容再优秀,如果搜索引擎的爬虫无法抓取到你的页面,那一切努力都将白费。而GPC爬虫池服务可以帮助你快速解决网站页面的收录问题。它通过千万级的爬虫池资源,强力…

【信创】银河麒麟安装程序记录

银河麒麟安装程序记录 Step1 添加外网yum源Step2 安装软件安装MySQL(8.0.32)安装JDK安装jdk17安装Nginx(1.21.5)安装Redis(4.0.14)安装Emqx(4.4.19)安装RocketMQ(5.1.4&a…

基于STM32的多功能MP3播放器

基于STM32的多功能MP3播放器 基于STM32的多功能MP3播放器一、项目背景与意义二、系统设计与实现2.1 硬件设计2.2 软件设计2.3 系统调试 三、功能实现与展望四、结论五、附录 基于STM32的多功能MP3播放器 在数字化时代,多功能播放器已成为我们生活中不可或缺的一部分…

Maven骨灰级教程:从小白到高手的终极指南(一)

目录 1. Maven的概念 1.1 什么是Maven 1.2 什么是依赖管理 1.3 什么是项目构建 1.4 Maven的应用场景 1.5 为什么使用Maven 1.6 Maven模型 2.初识Maven 2.1 Maven安装 2.1.1 安装准备 2.1.2 Maven安装目录分析 2.1.3 Maven的环境变量 2.2 Maven的第一个项目 2.2.1…

学习threejs,THREE.ConvexGeometry凸包高级几何体

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️THREE.ConvexGeometry凸包高…

Http模块总体设计

在前面的文章中,我们已经实现了一个主从Reactor模型的服务器类,但是由于他只是一个服务器,不涉及任何业务以及协议的处理,那么为了方便用户使用,我们可以预先设置好协议的支持,只需要让用户进行业务处理就行…

【Qt】Windows下Qt连接DM数据库

环境信息:W11 Qt5.12及以上 dm8 QODBC达梦 Windows环境创建ODBC数据源 使用 ODBC 方法访问 DM 数据库服务器之前,必须先配置 ODBC 数据源 在控制面板Windows工具中显示ODBC数据源管理器 ODBC数据源管理器标签 用户 DSN:添加、删除或配置本…

jmeter中发送post请求遇到的问题

用jmeter发送post请求,把请求参数放在Body Data处,参数都写得正确,但没想到结果每次都报错,直接响应结果乱七八糟,改成用Parameters,反而不乱报错了。 上图 请求里如下 另外一些请求也是这样 这个响应结果也是错误的…

⌈ 传知代码 ⌋ 农作物病害分类(Web端实现)

💛前情提要💛 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间,对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

国家超算互联网测评——用一分钱的显卡做大模型微调

大家好,我是刘明,明志科技创始人,华为昇思MindSpore布道师。 技术上主攻前端开发、鸿蒙开发和AI算法研究。 努力为大家带来持续的技术分享,如果你也喜欢我的文章,就点个关注吧 背景信息 算力、算法、数据,…

Vulhub Rickdiculously Easy 靶机详解

项目地址 https://download.vulnhub.com/rickdiculouslyeasy/RickdiculouslyEasy.zip实验过程 将下载好的靶机导入到VMware中,设置网络模式为NAT模式,然后开启靶机虚拟机 使用nmap进行主机发现,获取靶机IP地址 nmap 192.168.47.1-254根据对…

1971. 寻找图中是否存在路径

有一个具有 n 个顶点的 双向 图,其中每个顶点标记从 0 到 n - 1(包含 0 和 n - 1)。图中的边用一个二维整数数组 edges 表示,其中 edges[i] [ui, vi] 表示顶点 ui 和顶点 vi 之间的双向边。 每个顶点对由 最多一条 边连接&#x…

uniapp 常用的地区行业各种多选多选,支持回显,复制粘贴可使用

uniapp 常用的地区行业各种多选多选,支持回显 必须导入uni-popup 弹出层 该组件 1.目前项目开发中使用到这类似挺多的,记录一下,方便以后是使用 2.使用前提,目前不做无限级,只支持二维数组,模板里只循环了两…