写点什么

OBServer 启动恢复解析

发布于: 刚刚

作者:令川,一个在 OceanBase 专注打造稳定可靠好用存储引擎的研发同学。


OceanBase 是一个单进程软件,进程名叫:observer。

本文简单介绍进程 observer 启动后的逻辑,里面包含进程恢复的逻辑。本文对排查进程 observer 启动失败原因有一定参考作用。

目录结构

在标准配置下,observer 进程的执行目录结构如下所示,介绍其中几个比较关键的目录和其中的文件内容。

1.bin 目录下存放是 observer 以及一些其他工具的二进制文件

2.etc 目录下最主要的是的配置项文件,尤其是进程的一些启动参数

3.log 目录下是进程运行日志,通常用于排查问题或反映系统运行的记录。

4.store 目录下存放的是存储引擎关系最紧密的日志和数据文件了。它们的目录通常会是一个软连接,用于单独挂载磁盘。

日志目录有 3 个,分别是 clog/ilog/slog。clog 是数据的 commit log 或 redo log。ilog 是 clog 的索引,暂时可以不用了解。slog 是存储引擎的 redo log,是元数据的日志。三种日志的文件都是一组以单调递增序号为名字的文件。

sstable 目录顾名思义,存放的就是我们的数据啦。OceanBase 和其他多文件数据库不同,使用统一的 2M 块来组织和管理数据和元数据。因此在 sstable 目录下,会看到一个名为 block_file 的大文件。

$ob_dir├── bin│   ├── ob_admin│   ├── obproxy│   └── observer├── etc│   ├── io_resource.conf│   └── observer.config.bin├── lib│   ├── libmysqlclient.so.18│   └── libstdc++.so.6├── log│   ├── election.log│   ├── observer.log│   └── rootservice.log└── store    ├── clog -> /data/1/clog    │   └── 1    ├── ilog -> /data/1/clog    │   └── 1    ├── slog -> /data/2/clog    │   └── 1    └── sstable -> /data/2/clog        └── block_file
复制代码

OB 的 WAL

在介绍启动恢复流程前,简单介绍下 OceanBase 的 WAL 机制。WAL 机制的原理就不重复展开了。在 OceanBase 里,元数据和数据的 WAL 日志是分开的,即上一节在目录结构下看到的 slog 和 clog。

slog 是单机引擎日志,当在节点上创建修改或是销毁分区、生成新 sstable、移除旧 sstable 等元数据结构修改发生时,都会先写 slog,而后修改内存中的元数据。后台定期会将分区和 sstable 的元数据以快照的形式写在 2M 块上,也就是 slog 的 checkpoint 机制。

clog 是数据的 redo log。当有数据修改发生时,请求会到达 leader 副本所在节点,leader 会通过 paxos 协议先将日志同步写到其他 follower 的副本节点上。当有多数派节点的日志写盘成功后,数据修改成功,会再插入到内存中的 memory table 上。LSM Tree 架构下,当 memory table 到达阈值后,会触发冻结和转储,数据会以 sstable 的形式写在 block_file 的宏块内。而此时的 clog 回放位点会推进,类似于做了 checkpoint。

因为 clog 涉及了事务和 paxos 协议,需要在多个副本间协商和同步。而 slog 是单机引擎的元数据日志,不涉及多节点的同步和一致性问题。因此 clog 和 slog 在物理和实现上是分开的两套日志。

数据恢复流程解析

用一句话简单描述 OBServer 的启动恢复,就是要将 store 目录下的日志和数据一字不差的还原到内存中,将进程的状态恢复到宕机前的状态。在介绍具体的流程前,我们先了解下元数据和数据在磁盘上的组织格式。

快照组织格式

在内存中,observer 进程是以 partition-table store-sstable 这三层对象来管理数据的。因为一些历史原因,目前 partition 对象在代码中以ObPartitonGroup来实现;table store 是分区内的 memory table 和 sstable 集合,代码实现在ObTableStore。SSTable 则对应一组数据宏块(ObSSTable类)。

前面我们提到 OceanBase 在磁盘上使用 2M 大小的宏块来存放元数据和数据。当触发元数据快照时,每个分区都会调用各自的serialize接口,将序列化数据写到宏块内,这些块我们称之为 meta block。一个 meta block 只有 2M,不一定能存下所有分区的元数据。因此所有 meta block 都会以链表的形式串联起来,直到最后一个块,我们称之为入口块ObSuperBlockMetaEntry。入口块最终会持久化在 block_file 的第 1 和第 2 个宏块上,这两个宏块我们称为 super block。super block 上还会记录一些其他信息,例如 slog 的起始回放位点等等。



struct ObSuperBlockMetaEntry {  blocksstable::MacroBlockId macro_block_id_;  // first entry meta macro block id};
struct ServerSuperBlockContent {int64_t create_timestamp_; // create timestampint64_t modify_timestamp_; // last modified timestampint64_t macro_block_size_; int64_t total_macro_block_count_; int64_t total_file_size_;
common::ObLogCursor replay_start_point_; ObSuperBlockMetaEntry super_block_meta_; ObSuperBlockMetaEntry tenant_config_meta_;};
struct ObServerSuperBlock { ObSuperBlockHeaderV2 header_; ServerSuperBlockContent content_;};
复制代码

因此,当 server 启动时,会先从 block_file 的第 1 和第 2 个 super block 中读出上一次快照的元数据入口块,接着从这些入口块中,逐个调用 partition、table store 和 sstable 的deserialize接口,将对象恢复到内存中去。

启动流程

observer 进程的启动代码从 main 函数里一目了然,分为三个阶段:初始化、启动、运行。看过或熟悉 OB 代码的同学对于 init/start/wait 这三个接口不会陌生。最主要的几大组件都会有这三个接口,分别在上述的三个阶段中被调用。

ObServer& observer = ObServer::get_instance();if (OB_FAIL(observer.init(opts, log_cfg))) {    LOG_ERROR("observer init fail", K(ret));} else if (OB_FAIL(observer.start())) {    LOG_ERROR("observer start fail", K(ret));} else if (OB_FAIL(observer.wait())) {    LOG_ERROR("observer wait fail", K(ret));}
复制代码

每个组件的 start 流程就不展开讲了。数据和日志恢复的代码入口在ob_partition_service.cpp文件的ObPartitionService::start()方法内。主要流程参见下图,1)加载元数据的快照点,即将分区和 sstable 的信息还原进内存。2)接着,回放 slog,将分区和 sstable 的信息更新到最新状态。3)然后,从分区的元信息中获取 clog 的回放位点,开始回放 clog 日志生成 memory table。上述三步完成后,单机数据的恢复流程就告一段落了。

因此 OceanBase 利用 WAL 和快照机制相结合,保证了节点在宕机后,仍然能够从磁盘的持久化信息中心恢复出宕机前的状态,保证了数据的完整性。



代码参考

元数据快照和加载代码实现,可以参见ObServerCheckpointWriterObServerCheckpointLogReader类。

slog 的重启回放代码主要在ObStorageLogReplayer类中。

clog 的重启回放代码相对会复杂一些,有兴趣的同学可以挖一下ObLogScanRunnable这个类。

slog 的读写库参见ObStorageLogReaderObStorageLogWriter

clog 的读写库参见ObLogDirectReaderObClogWriter


今天我们随着 OBServer 的启动流程,探寻了 OB 的 WAL 和快照机制,了解了存储引擎内部多个层次对象,以及它们是如何协同工作保证数据的完整性。过程中,还有许多点并没有展开去讲,其中有不少有趣之处可再细细介绍。欢迎你也加入 OceanBase,探索数据库的乐趣,一起做最先进的分布式数据库!


最后的最后:如果您有任何疑问,可以通过以下方式与我们进行交流:

微信群:扫码添加小助手,将拉你进群哟~


钉钉群:33254054


今日之星,明日之星都不如你留下的星星(⭐️️)

我们想让 Github 上优质的开源项目被更多人看到。

文档都是我们精心整理。如果有帮助的话求个 star(◕ᴗ◕✿),鼓励鼓励我们哟!

也欢迎大家给我们 issue,请点击 这里。运营小姐姐在此跪谢️️ ❥(^_-)

欢迎大家一起参与社区贡献,指南请参考看 这里

社区答疑:请点击 这里

发布于: 刚刚阅读数: 2
用户头像

OceanBase 社区版 2020.05.06 加入

github:https://github.com/oceanbase/oceanbase 欢迎大家

评论

发布
暂无评论
OBServer启动恢复解析