当前位置: 首页 > news >正文

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

背景

在数据库开发过程中,“写盘”是一项核心操作,即将内存中暂存的数据安全地转储到磁盘上。在诸如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_;  // 事务idshare::SCN trans_version_; // 事务提交版本share::SCN scn_; // 数据所在日志的 scnint64_t seq_no_;  // 在事务中的sql numble // 可能一个存储过程有多个相同sql no的mvcc nodeshare::SCN tx_end_scn_; // 数据所在事务的 commit/abort 日志的 scn // 未提交为max...uint8_t type_;  // 标记是否是COMPACTuint8_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上找到),只有真正的去探究了源码,才能领略到其中的艰涩与风光。当然,笔者文中难免有不少纰漏,也欢迎大家指正和讨论,错误的订正也是加深理解的一个过程。

相关文章:

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

背景 在数据库开发过程中,“写盘”是一项核心操作,即将内存中暂存的数据安全地转储到磁盘上。在诸如MySQL这样的传统数据库管理系统中,写盘主要有以下几步:首先将数据写入缓存池;其次,为了确保数据的完整性…...

用Java爬虫API,轻松获取taobao商品SKU信息

在电子商务的世界里,SKU(Stock Keeping Unit,库存单位)是商品管理的基础。对于商家来说,SKU的详细信息对于库存管理、价格策略制定、市场分析等都有着重要作用。taobao作为中国最大的电子商务平台之一,提供…...

OpenHarmony 入门——ArkUI 自定义组件内同步的装饰器@State小结(二)

文章大纲 引言一、组件内状态装饰器State1、初始化2、使用规则3、变量的传递/访问规则说明4、支持的观察变化的场景5、State 变量的值初始化和更新机制6、State支持联合类型实例 引言 前一篇文章OpenHarmony 入门——ArkUI 自定义组件之间的状态装饰器小结(一&…...

【Linux驱动开发】嵌入式Linux驱动开发基本步骤,字符设备开发入门,点亮LED

【Linux驱动开发】嵌入式Linux驱动开发基本步骤,字符设备开发入门,点亮LED 文章目录 开发环境驱动文件编译驱动安装驱动自动创建设备节点文件 驱动开发驱动设备号地址映射,虚拟内存和硬件内存地址字符驱动旧字符驱动新字符驱动 应用程序开发…...

搬砖14、Python网络编程入门

网络编程入门 计算机网络基础 计算机网络是独立自主的计算机互联而成的系统的总称,组建计算机网络最主要的目的是实现多台计算机之间的通信和资源共享。今天计算机网络中的设备和计算机网络的用户已经多得不可计数,而计算机网络也可以称得上是一个“复…...

Transformer: Attention is All you need

Transformer Transformer是基于Encoder-Decoder结构的,将Seq2Seq中的RNN/GRU部分更换为Self-Attention部分 位置编码 Positional Encoding Self-attention丢失了位置信息 CNN 卷积神经网络可以保存相邻的位置信息 RNN 是顺序输入的,是包含了位置信息…...

C++:排序算法

目录 一、插入排序 1.直接插入排序 2.希尔排序 二、交换排序 1.冒泡排序 2.快速排序 三、选择排序 1.简单选择排序 2.堆排序 四、归并排序 1.二路归并排序的递归实现 2.二路归并排序的非递归实现 一、插入排序 1.直接插入排序 直接插入排序的基本思想是&#xff…...

期货日内稳赢策略:双15交易法详解

Eagle Trader的考试不仅涵盖了CFD交易,期货交易的考生人数也颇为可观。与外汇市场相比,期货在国内市场的普及程度更高,参与的群体也更为广泛。这得益于期货市场在国内相对成熟的监管体系,使得交易员对期货有了更深入的了解和信任。…...

2024年10月第2个交易周收盘总结:怎样卖出!

计划自己的交易,交易自己的计划。 跟随市场而情绪波动,最终一定会导向失败! 连续、平稳、冷静地惯彻交易计划,比什么都重要! 交易本身是极其简单和清楚的,让事情变复杂的原因不是行情走势和交易本身&…...

mysql 不支持utf8mb4_0900_ai_ci

Unknowncollation:‘utf8mb4_0900_ai_ci’ 解决方案: 1. 升级mysql为8.0以上(不包含8.0) 2. 修改编码类型: utf8mb4_0900_ai_ci/utf8mb4_0900_ci 修改为utf8_general_ci utf8mb4修改为utf8 utf8mb4_0900_ai_ci 是一种 MySQL 数…...

第10篇:防火墙与入侵检测系统

目录 引言 10.1 防火墙的基本概念 10.2 防火墙的分类 10.3 防火墙策略的配置与实现 10.4 入侵检测系统(IDS) 10.5 防火墙与IDS的结合 10.6 总结 第10篇:防火墙与入侵检测系统 引言 在当今的数字世界中,网络安全已经成为企…...

Jmeter监控服务器性能

目录 ServerAgent 安装 打开Jmeter ServerAgent 在Jmeter上监控服务器的性能比如CPU,内存等我们需要用到ServerAgent,这里可以下载我分享 ServerAgent-2.2.3.zip 链接: https://pan.baidu.com/s/1oZKsJGnrZx3iyt15DP1IYA?pwdedhs 提取码: edhs 安装…...

通过前端UI界面创建VUE项目

通过前端UI界面创建VUE项目,是比较方面的一种方式,下面我们详细分析一下流程: 1、找到合适目录 右键鼠标,点击在终端打开 2、开始创建 输入 vue ui 浏览器弹出页面 3、点击Create项目 显示已有文件列表,另外可以点击…...

Python网络爬虫:分析淘宝商品热度与销量[进阶深度优化]

要更全面和深入地介绍基于Python的网络爬虫系统,分析淘宝商品买卖热度、销量以及统计热点关键词,我们可以进一步扩展内容,涵盖更多技术细节、优化策略、数据分析、以及机器学习的结合,形成一个功能强大、可靠的爬虫系统。下面是进一步的补充。 1. 爬虫策略的深度优化 为了…...

golang从http请求中读取xml格式的body,并转成json

推荐学习文档 golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔…...

RestTemplate 学习笔记

简介 RestTemplate是一个执行HTTP请求的同步阻塞式工具类,它仅仅只是在 HTTP 客户端库(例如 JDK HttpURLConnection,Apache HttpComponents,okHttp 等)基础上,封装了更加简单易用的模板方法 API&#xff0c…...

数据抓取时,使用动态IP要注意哪些?

在充满竞争和数据驱动的商业环境中,动态IP已成为数据抓取过程中不可或缺的工具。动态IP的应用能有效提高抓取成功率,但同时也伴随着一系列需要注意的问题。在本文中,我们将详细探讨在数据抓取时使用动态IP时应注意的事项,以确保抓…...

C++类的构造函数

1、what 类的特殊成员函数,用来初始化类对象的数据成员。 只要类对象被创建,就会被执行。 构造函数的名字和类名相同,可以包含“0”个(其实有一个编译器生成的合成默认构造函数,只是看不见而已)、1个或多个构造函数,没有返回值,不同构造函数使用参数数量或参数类型进行…...

第21~22周Java主流框架入门-Spring 3.SpringJDBC事务管理

Spring JDBC模块与事务管理课程总结 1. 课程介绍 本课程主要讲解Spring框架中的JDBC模块及其事务管理的相关内容,重点包括以下三个方面: Spring JDBC模块及核心对象JDBC Template的使用 通过学习如何使用Spring JDBC模块,了解JDBC Template…...

C++ —— 类和对象

目录 介绍类和对象 一. 类和对象——类的定义 1.访问限定符 2.类域 作用操作符:: 3.对象大小 类的实例化 内存对齐规则 4.this指针 this指针会出现的问题 5.C语言结构体与C类对比 封装的本质 C类的优点 二 .类和对象——关于成员 1.类的默认成员函数 I.构造函数 构…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...

测试markdown--肇兴

day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes&#xff0…...

Ascend NPU上适配Step-Audio模型

1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...

Netty从入门到进阶(二)

二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...

站群服务器的应用场景都有哪些?

站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...