作者简介:镜水,一个致力于无限进步的数据库学徒。
作者简介:海芊,一个致力于当网红的 OceanBase 文档工程师。个人频道:Amber loves OB
本文主要介绍 OceanBase 数据库启动时是如何将已持久化的日志和数据恢复到内存,重新形成各类信息(如租户信息、分区信息等)的内存映像,从而回到宕机前的状态。
在介绍具体的恢复流程之前,我们首先来了解一些与之相关的存储结构。
存储数据结构
MacroBlock
OceanBase 数据库将数据分为增量数据和基线数据,基线数据是几乎占满整个磁盘的一个超大文件,OceanBase 数据库以固定大小的宏块(MacroBlock,默认大小为 2 MB)为单位对磁盘上的基线数据进行管理,从而优化每日合并过程并高效利用磁盘空间,这类似于操作系统中内存的页式管理。宏块按照其在磁盘中的位置进行逻辑编号,起始编号为 0。
MacroBlockType
枚举类型标识了宏块有哪些种类:
enum MacroBlockType {
Free = 0, // 空闲宏块
SSTableData = 1, // 数据宏块,实际存放用户数据
PartitionMeta = 2, // 分区元数据宏块,通常以宏块链表的方式进行存储
// SchemaData = 3,
// Compressor = 4,
MacroMeta = 5, // 宏块元数据宏块,通常以宏块链表的方式进行存储
Reserved = 6,
MacroBlockSecondIndex = 7, // deprecated
SortTempData = 8,
LobData = 9,
LobIndex = 10,
TableMgrMeta = 11, // 新版本已废弃
TenantConfigMeta = 12, // 租户配置元数据宏块,通常以宏块链表的方式进行存储
BloomFilterData = 13,
MaxMacroType,
};
除此之外,类似于文件系统,宏块还有一种称为 Super Block
的特殊种类。Super Block
存放了整个基线数据的关键信息,比如元数据入口点和日志回放点,固定为第 0 块宏块,通常还有若干个备份块。
除了 Super Block
以外每个宏块都有一个 ObMacroBlockCommonHeader
。
struct ObMacroBlockCommonHeader {
int32_t header_size_; // struct size
int32_t version_; // header version
int32_t magic_; // magic number
// each bits of the lower 8 bits represents:
// is_free, sstable meta, tablet meta, compressor name, macro block meta, 0, 0, 0
int32_t attr_; // MacroBlockType
union {
uint64_t data_version_; // sstable macro block: major version(48 bits) + minor version(16 bits)
/**
* For meta block, it is organized as a link block. The next block has a index point to
* its previous block. The entry block index is store in upper layer meta (super block)
* 0 or -1 means no previous block
*/
int64_t previous_block_index_;
};
union {
int64_t reserved_;
struct {
int32_t payload_size_; // not include the size of common header
int32_t payload_checksum_; // crc64 of payload
};
};
};
NOTE:previous_block_index_
为前向指针,通常为链式结构的元数据宏块,它表明从新到旧的一个前向关系。例如,下表所示宏块链表的写入顺序为 12345,但由于前向链接,读取时的入口点为 5,从而导致读取顺序为 54321,因此实际使用数据时,通常需要一次正向(54321)读取获取 block id
的数组后,再进行一次反向(12345)读取,从而得到正确的数据写入顺序。
在了解了数据块的基本单元 Macro Block
后,我们接着来介绍 Super Block
以及 Meta Block
。
由于历史原因,目前对于super block
以及meta block
的组织方式有新旧两个版本之分,下面我们将这两个版本分开介绍。
旧版本:
旧版本的 Super Block
和所有的 Meta Block
都在同一个大文件(如果对 observer 的执行目录有所了解的话,这个大文件可以理解为 sstable 目录下的 block_file)上。
Super Block
Super Block
可以简单理解为所有持久化数据的普遍元数据,其中包含了各种 Meta Block
的入口块(即宏块链表第一块)以及 SLog 的回放入口点(SLog 文件中的日志经过 checkpoint 后成为 Meta Block
,这里的回放入口点即 SLog 中尚未经过 checkpoint 形成 meta 的日志偏移位置,如果将 Meta Block
看作 meta 的基线数据,那么SLog 需要回放的日志可以理解为 meta 的增量数据),一般是前两个 macro block
。
struct ObSuperBlockHeader {
static const int64_t OB_MAX_SUPER_BLOCK_SIZE = 64 * 1024;
int32_t super_block_size_; // not used any more
int32_t version_;
int32_t magic_; // magic number
int32_t attr_; // reserved, set 0
};
struct ObSuperBlockV2 {
struct MetaEntry {
static const int64_t META_ENTRY_VERSION = 1;
int64_t block_index_; // first entry meta macro block id
int64_t log_seq_; // replay log seq
int64_t file_id_; // ofs file id
int64_t file_size_; // ofs file size
};
struct SuperBlockContent {
static const int64_t SUPER_BLOCK_CONTENT_VERSION = 2;
int64_t create_timestamp_; // create timestamp
int64_t modify_timestamp_; // last modified timestamp
int64_t macro_block_size_;
int64_t total_macro_block_count_;
int64_t free_macro_block_count_;
int64_t total_file_size_;
// entry of macro block meta blocks,
common::ObLogCursor replay_start_point_; // SLog 回放入口点
MetaEntry macro_block_meta_; // macro block 元数据回放入口点
MetaEntry partition_meta_; // partition 元数据回放入口点
MetaEntry table_mgr_meta_; // table mgr 元数据回放入口点
MetaEntry tenant_config_meta_; // tenant config 元数据回放入口点
};
ObSuperBlockHeader he