一文读懂 OceanBase 数据库的启动恢复代码解析
作者简介:镜水,一个致力于无限进步的数据库学徒。
作者简介:海芊,一个致力于当网红的 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
)上。
<strong>Super Block</strong>
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 moreint32_t version_; int32_t magic_; // magic numberint32_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 idint64_t log_seq_; // replay log seqint64_t file_id_; // ofs file idint64_t file_size_; // ofs file size }; struct SuperBlockContent { static const int64_t SUPER_BLOCK_CONTENT_VERSION = 2; int64_t create_timestamp_; // create timestampint64_t modify_timestamp_; // last modified timestampint64_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 header_; SuperBlockContent content_; }
由于 Meta Block
是以前向链表的形式连接的,每个 Meta Block
除了 ObMacroBlockCommonHeader
以外还包含一个 link header
:
struct ObLinkedMacroBlockHeader {int32_t header_size_; int32_t version_; int32_t magic_; int32_t attr_; // MacroBlockTypeint64_t meta_data_offset_; // data offset base on current headerint64_t meta_data_count_; // 当前 block 有多少个元数据 item // 如果是 0,表示该 item 超出 2 M,那么多个 block 构成多个完整 item // 否则,1 个 block 包含多个完整 itemint64_t previous_block_index_; // last link blockint64_t total_previous_count_; // 之前所有 block 所包含的元数据 item 总数int64_t user_data1_; // log_seq_num,not usedint64_t user_data2_; // 如果多个 block 组成多个 item,该值表示这多个 block 所包含的多个 item 的总数据序列化长度 }
<strong>MacroBlockMeta</strong>
宏块元数据保存了数据宏块中数据列排布、微块索引等重要元数据,从磁盘加载后常驻内存并在内存中维护。
所有宏块元数据(macro block meta
,也就是 item)由多个 macro block
组成,每个 block 可能有多个 item(完整的,由于一个 item 不会跨多个 block,可能存在 padding 空间),每个 item 代表了 block id
从 0 开始的每一个 macro block
的元数据,也就是说每个 macro block
都有一个 macro block meta
。
每个 item 是 ObMacroBlockMeta
结构,解析出来的每个 item 在内存里存放在一个类似于 hash table 的结构里,通过 block id 进行 hash,解析代码见 ObMacroMetaBlockReader::parse
。
<strong>TableMgrMeta</strong>
表管理元数据保存了 SSTable 的相关元信息。
TableMgrMeta
由多个macro block
组成,可能每个 block 有多个 item(完整的,这种情况下block可能存在 padding 空间),也可能多个 block 组成多个 item(比如,3个 block 组成 4 个 item,那么前两个 block 的 linked_header.meta_data_count_
为 0,最后一个 block 的 linked_header.meta_data_count_
为 4,第一个 block 的 linked_header.user_data2_
代表了这 4 个 item 的总序列化大小,第二和第三个 bloc k的 linked_header.user_data2_
则为 0),而一次对多个 item 的读取(一个 block,或者多个 block)实际对应着一条 log 记录,是同一个 log_seq_num
。
每个 item 是 ObOldSSTable
结构,包含了一个 sstable 的信息,解析代码见 ObTableMgrMetaBlockReader::parse
。解析一条 log 中的多个 item 用的是 ObTableMgr::load_sstable
。
每个 ObOldSSTable
有一个 table_key
:
struct TableKey { ObITable::TableType table_type_; common::ObPartitionKey pkey_; uint64_t table_id_; // combine bits // include tenant_id (24 bits) common::ObVersionRange trans_version_range_; common::ObVersion version_; // only used for major merge common::ObLogTsRange log_ts_range_; }
<strong>PartitionMeta</strong>
分区元数据由多个 macro block
组成,可能每个 block 有多个 item(完整的),也可能多个 block 组成多个 item(组成方式和 TableMgrMeta
相同),一次对多个 item 的读取同样对应着同一条 log 记录,解析一条 log 中的多个 item 用的是 ObPartitionMetaRedoModule::load_partition
。
每个 item 是 ObPartitionGroup
结构,包含了 partition 的全部基线信息,包括 sstable 信息。
解析代码见 ObPartitionMetaBlockReader::parse
,解析后每个 partition 的 sstable 会存放在其自身的 ObPGStorage pg_storage_.sstable_mgr_.table_map_
。
NOTE:PartitionGroup(简称 PG)和 Partition 的概念可能会增加代码阅读复杂度,可以简单理解一个 PG 里面只有一个 Partition,只有特殊情况 PG 才会包含多个 Partition,看代码时可以简单把 PG 看作 Partition。
<strong>TenantConfigMeta</strong>
租户配置元数据由多个 macro block
组成,每个 block 可能有多个 item(完整的),每个 item是一个容纳了所有租户配置项的数组(TenantUnits
结构)。
每个item在ObTenantConfigMgr::load_tenant_units
载入时,会reset
原来的配置项数组,相当于覆盖更新。
解析代码见 ObTenantConfigMetaBlockReader::parse
。
新版本:
旧版本 Super Block
和所有的 meta block
都在同一个文件上,没有为不同的 tenant
做区分。而新版本则不一样,每个 tenant
有单独的 Super Block
和 meta block
,其中每个 tenant
的 Super Block
(第二级 Super Block
)是一个 SuperBlockMeta
(第一级 meta)item,和第一级 Super Block
在同一个文件,而每个 tenant
的 meta block
在 其单独的文件中,文件 id 以及元数据的入口点保存在 tenant
自己的 Super Block
中。
简单来说,通过第一级 Super Block
能够找到第一级 meta(SuperBlockMeta
)的入口点,而通过第一级 meta 能够找到每个 tenant
对应的单独文件以及对应的二级 Super Block
,然后通过二级 Super Block
上的入口点能够得到每个 tenant
的单独文件 id 以及文件上的二级 meta block
。
<strong>Super Block</strong>
这是第一级 Super Block
,针对所有的 tenant
。
struct ObSuperBlockMetaEntry { blocksstable::MacroBlockId macro_block_id_; // first entry meta macro block id }; struct ObSuperBlockHeaderV2 { int32_t version_; int32_t magic_; // magic number }; struct ObServerSuperBlock {struct ServerSuperBlockContent { static const int64_t SUPER_BLOCK_CONTENT_VERSION = 1; 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_; // SLog 回放入口点 ObSuperBlockMetaEntry super_block_meta_; // tenant spuer block 元数据回放入口点 ObSuperBlockMetaEntry tenant_config_meta_; // tenant config 元数据回放入口点 }; ObSuperBlockHeaderV2 header_; ServerSuperBlockContent content_; };
Meta Block
除了 ObMacroBlockCommonHeader
以外还包含一个 link header
:
struct ObLinkedMacroBlockHeaderV2 { int32_t header_size_; int32_t version_; int32_t magic_; int32_t attr_; int32_t item_count_; // 该 block 有多少个 item // 如果是 0,表示该 item 超出 2M,那么多个 block 构成多个完整 item // 否则,1 个block 包含多个完整 itemint32_t fragment_offset_; // record previous macro block's MacroBlockId // 分别是 MacroBlockId 的四个部分int64_t previous_block_first_id_; int64_t previous_block_second_id_; int64_t previous_block_third_id_; int64_t previous_block_fourth_id_; }
新版本的每个 meta item 都包含一个 meta header
:
struct ObPGMetaItemHeader { int16_t type_; int16_t reserved_; int32_t size_; // item 的数据长度,不带 ObPGMetaItemHeader 的长度 };
<strong>TenantConfigMeta</strong>
新版本租户配置元数据和旧版本含义一致,解析代码见 ObTenantConfigMetaCheckpointReader::read_checkpoint
。
每个 item 是 TenantUnits
结构,每个 item 也是调用 ObTenantConfigMgr::load_tenant_units
载入。
<strong>SuperBlockMeta</strong>
这是第一级 meta,由多个 macro block
构成,可能每个 block 有多个i tem(完整的),也可能多个 block 组成多个 item。
新版本和旧版本对 item 的拆分方式不太一样,旧版本的拆分方式可以见 TableMgrMeta
的介绍,新版本则为拆分封装了更规范的函数(ObPGMetaItemReader::get_next_item
等),通过 item_count_
来判断一个 block 是否包含完整的 item,不再通过 linked_header.user_data2_
来判断多个 item 的总长度(该成员新版本已不存在),而是通过 item_header.size_
来 判断每个 item 的长度,如果一个 item 跨多个 block,则根据 item 的长度和每个block的有效数据长度(common_header.payload_size_
)来拼接得到整个 item 的有效数据。
解析代码见 ObTenantFileSuperBlockCheckpointReader::read_checkpoint
,每个 item 由ObPGMetaItemReader::get_next_item
依次获取,每个 item 是 ObTenantFileSuperBlockCheckpointEntry
结构,包含了一个 ObTenantFileInfo
,ObTenantFileInfo
包含了一个 tenant
单独文件的 id 以及第二级 Super Block
,由此可以得到每个 tenant
对应的单独文件以及二级元数据的入口点。
struct ObTenantFileKey {
uint64_t tenant_id_;
int64_t file_id_; // 每个 tenant 单独文件的文件 id, 二级 meta block 存储在该文件上
};
struct ObTenantFileSuperBlock {
blocksstable::ObSuperBlockMetaEntry macro_meta_entry_; // 二级 meta, macro block meta
blocksstable::ObSuperBlockMetaEntry pg_meta_entry_; // 二级 meta, pg meta
ObTenantFileStatus status_;
bool is_sys_table_file_;
};
struct ObTenantFileInfo {
ObTenantFileKey tenant_key_;
ObTenantFileSuperBlock tenant_file_super_block_; // 一级 meta, 每个 tenant 单独的 super block
PG_MAP pg_map_;
};
<strong>MacroBlockMeta</strong>
宏块元数据是第二级 meta,针对的是某个 tenant
,由多个 macro block
组成,每个 block 可能有多个item(完整的),每个 item 是 ObPGMacroBlockMetaCheckpointEntry
结构,其中包含的 meta_
不再是旧版本的 ObMacroBlockMeta
结构,而是 ObMacroBlockMetaV2
。
解析出来的所有 item 存放在一个 hashmap 里,key 是 ObMacroBlockKey
结构,解析代码见 ObPGMacroMetaCheckpointReader::read_checkpoint
。
struct ObPGMacroBlockMetaCheckpointEntry { int64_t disk_no_; blocksstable::MacroBlockId macro_block_id_; ObITable::TableKey table_key_; blocksstable::ObMacroBlockMetaV2& meta_; }; struct ObMacroBlockKey { // 引入 table key 可以在检查时判断该 sstable 是否还存在,不存在那么 macro block 自然也失效了 ObITable::TableKey table_key_; // 这里的 block id 是每个 sstable 里宏块的逻辑 id,对于每一个 sstable 都是从头开始 blocksstable::MacroBlockId macro_block_id_; };
<strong>PGMeta</strong>
分区组元数据是第二级 meta,针对的是某个 tenant
,但和旧版本的 PartitionMeta
含义基本一致,依然由多个 macro block
组成,可能每个 block 有多个 item(完整的),也可能多个 block 组成多个 item,解析代码见 ObPGMetaCheckpointReader::read_checkpoint
。
每个 item 是 ObPartitionGroup
结构,包含了 partition 的全部基线信息,包括 sstable 信息。每个 item 载入内存的过程依然是调用 ObPartitionMetaRedoModule::load_partition
。
SLog
SLog是meta的增量数据,相关介绍可以阅读 SLog结构介绍 文档,这里我们简单介绍几种具体的SLog日志类型。
<strong>MacroBlockMeta SLog(main_type = OB_REDO_LOG_MACROBLOCK)</strong>
该日志对应于旧版本的MacroBlockMeta
,新版本不再使用。
只有一种日志类型:CHANGE_MACRO_BLOCK_META
CHANGE_MACRO_BLOCK_META
:某个macro block的元数据发生了变更。每个log entry是ObMacroBlockMetaLogEntry
结构,包含block_id和ObMacroBlockMeta
,意味着整个新Macro Block
元数据都在log里。
struct ObMacroBlockMetaLogEntry { int64_t data_file_id_; int64_t disk_no_; int64_t block_index_; ObMacroBlockMeta& meta_ptr_; };
<strong>TableMgr SLog(main_type = OB_REDO_LOG_TABLE_MGR)</strong>
该日志对应于旧版本的 TableMgrMeta
,新版本不再使用。
包含三种日志:REDO_LOG_CREATE_SSTABLE
、REDO_LOG_COMPELTE_SSTABLE
和 REDO_LOG_DELETE_SSTABLE
。
REDO_LOG_CREATE_SSTABLE
:不支持。
REDO_LOG_COMPELTE_SSTABLE
:增加了一个 sstable。每个 log entry 是 ObCompleteSSTableLogEntry
结构,包含了 table_key 和 ObOldSSTable
。
struct ObCompleteSSTableLogEntry {
ObOldSSTable& sstable_;
};
REDO_LOG_DELETE_SSTABLE
:删除了一个 sstable。每个 log entry 是 ObDeleteSSTableLogEntry
结构,包含了 table_key。
struct ObDeleteSSTableLogEntry {
ObITable::TableKey table_key_;
};
<strong>TenantConfigMeta Slog (main_type = OB_REDO_LOG_TENANT_CONFIG)</strong>
该日志新旧版本都使用。
只有一种日志类型:REDO_LOG_UPDATE_TENANT_CONFIG
。
REDO_LOG_UPDATE_TENANT_CONFIG
:租户配置发生变更。每个 log entry 是 ObUpdateTenantConfigLogEntry
结构,包含 TenantUnits
,replay 时会将原有 tenant_units_
重置并全部更新,意味着即使只有一个租户配置变化,log 里还是保存了所有租户的配置。
struct ObUpdateTenantConfigLogEntry {
share::TenantUnits& units_;
};
<strong>PartitionMeta SLog (main_type = OB_REDO_LOG_PARTITION)</strong>
该日志新旧版本都使用,并且旧版本的部分日志类型迁移到了该日志下。
由于日志种类比较多,这里只介绍部分日志类型,感兴趣的同学可以进一步阅读代码(ObPartitionMetaRedoModule::replay
)。
REDO_LOG_ADD_PARTITION/REDO_LOG_ADD_PARTITION_GROUP/REDO_LOG_ADD_PARTITION_TO_PG
:增加 partition 或者 pg。每个 log entry 是 ObChangePartitionLogEntry
结构,只包含了ObPartitionKey
/ObPGKey
等标识信息。
struct ObChangePartitionLogEntry { common::ObPartitionKey partition_key_; common::ObReplicaType replica_type_; common::ObPGKey pg_key_; uint64_t log_id_; };
REDO_LOG_ADD_SSTABLE
:某 partition 增加了 sstable。每个 log entry 是 ObAddSSTableLogEntry
结构,包含 ObPGKey
和 ObSSTable
。replay 时会将 ObSSTable
添加到 ObPGStorage::sstable_mgr_
里的 table_map_
。
struct ObAddSSTableLogEntry { common::ObPGKey pg_key_; ObSSTable& sstable_; };
REDO_LOG_CHANGE_MACRO_META
:某个 macro block 的元数据发生了变更。该日志与旧版本的CHANGE_MACRO_BLOCK_META
类型日志一致。每个 log entry 是 ObPGMacroBlockMetaLogEntry
结构,包含 block id 和 ObMacroBlockMetaV2
。
struct ObPGMacroBlockMetaLogEntry { ObPGKey pg_key_; ObITable::TableKey table_key_; int64_t data_file_id_; int64_t disk_no_; blocksstable::MacroBlockId macro_block_id_; blocksstable::ObMacroBlockMetaV2& meta_; };
恢复流程
启动恢复过程的调用栈如下:
ObStoreFile::open(bool) >> ObLocalFileSystem::start() // 开源上是ObLocalFileSystem,可能还有其他FileSystem >>> ObPartitionService::start() >>>> ObServer::start() >>>>> main(int, char **)
主要分为三个流程:
1.加载元数据的快照点,即将分区和 sstable 的信息还原进内存
2.回放 slog,将分区和 sstable 的信息更新到最新状态
3.从分区的元信息中获取 clog 的回放位点,开始回放 clog 日志生成 memory table
其中前两步调用 ObStoreFile::read_checkpoint_and_replay_log
完成,这里只介绍这一过程,建议参照代码阅读。
<strong>ObStoreFile::read_checkpoint_and_replay_log</strong>
:
1.根据 super_block
的版本走不同的分支
2.v2 版本(旧版本),先读出 ObSuperBlockV2 super_block
,然后调用 ObServerCheckpointLogReaderV1::read_checkpoint_and_replay_log
3.v3 版本(新版本),直接调用 ObServerCheckpointLogReader::read_checkpoint_and_replay_log
<strong>ObServerCheckpointLogReaderV1::read_checkpoint_and_replay_log</strong>
:
1.首先 ObServerCheckpointLogReaderV1::read_checkpoint
读取所有的 meta block
(macro_meta
/table_mgr_meta
/partition_meta
/tenant_config_meta
),并使用对应元数据的类解析函数 parse
出相应的信息恢复到内存(比如 partition_meta
能够解析出每个分区的基础信息以及包含的 sstable 信息,将以某种结构保存到内存),然后将所有 meta block
的 block id
存到同一个数组,进而在内存维护每个 meta block 的引用状态。
2.接着 ObServerCheckpointLogReaderV1::replay_slog
恢复 SLog,首先根据 super block
中的 replay_start_point_
位置从 SLog 读取每一条 log,记录 begin 和 commit(ObStorageLogCommittedTransGetter::init
),然后 ObStorageLogReplayer::replay
进行日志回放,同样从 replay_start_point_
位置开始读 SLog,log 的 trans id 如果是已经 commited 的(上一步记录了),那么 redo 该日志,即调用该 log type 对应的 replay 函数。
<strong>ObServerCheckpointLogReader::read_checkpoint_and_replay_log</strong>
:
1.首先读出 ObServerSuperBlock super_block
(第一级 Super Block
)
2.从 super_block
获取 SLog 回放入口点 replay_start_point_
,从该位置读取每一条 log,记录 begin 和 commit(ObStorageLogCommittedTransGetter::init
)
3.
ObServerCheckpointLogReader::read_tenant_file_super_block_checkpoint
读取所有 tenant
的 super block meta
(这些 super block meta
相当于第一级 meta
,第二级的 super block
,和 super_block
在同一个文件上,入口点在 super_block
上 ,也是多个 macro block
前向链接而成):首先ObPGMetaBlockReader::init
从 super block meta
入口点读取所有的super block meta
(ObPGMetaBlockReader::get_meta_blocks
),并反向预取出第一个 meta block
的数据保存在内存,接着将反向读取每一个 meta block
;然后依次解析每个 meta item,将每个tenant
和其对应的file以map的形式保存在内存(ObBaseFileMgr::replay_alloc_file
)
4.
ObServerCheckpointLogReader::read_tenant_meta_checkpoint
读取所有 tenant config meta block
:首先 ObTenantConfigMetaCheckpointReader::init
从 super block meta
入口点读取所有的 super block meta
(ObPGMetaBlockReader::get_meta_blocks
),并反向预取出第一个 meta block
的数据保存在内存,接着将反向读取每一个 meta block
;然后依次解析每个 meta item ,不断重新覆盖 tenant config
(ObTenantConfigMgr::load_tenant_units
)
5.
ObServerCheckpointLogReader::replay_server_slog
回放第一级 meta(tenant 的 super block meta
和tenant config meta
)的 SLog,调用 ObStorageLogReplayer::replay
进行日志回放,从replay_start_point
位置开始读 SLog,log 的 trans id 如果是已经 commit 的(第 2 步记录了),那么 redo,调用相应的 replay 函数(只会对上述两种 meta 的 SLog 触发 replay)
6.
ObServerCheckpointLogReader::read_pg_meta_checkpoint
根据第 3 步得到的 map 依次回放每个tenant
的二级元数据,每个 tenant
的回放调用ObTenantFilePGMetaCheckpointReader::read_checkpoint
:首先从 tenent
的 file_info
能够得到 tenant_file_super_block_
,从而获得 macro meta
和 pg meta
的 block 入口点,然后首先通过ObPGMacroMetaCheckpointReader::read_checkpoint
读取 macro meta
并保存在一个从 tenant_mgr
得到的 replay_map
中,接着通过 ObPGMetaCheckpointReader::read_checkpoint
读取pg meta,每个item包含一个 pg 的信息,使用 ObPartitionMetaRedoModule::load_partition
将其解析并载入到内存
7.
ObServerCheckpointLogReader::replay_other_slog
回放第二级 meta(每个 tenant 的 macro meta
以及 pg meta
)的 SLog,调用 ObStorageLogReplayer::replay
进行日志回放,从replay_start_point
位置开始读 SLog,log 的 trans id 如果是已经 commit 的(第 2 步记录了),那么 redo,调用相应的 replay 函数(只会对上述两种 meta 的 SLog 触发 replay)
8.最后 ObServerCheckpointLogReader::set_meta_block_list
将所有meta block
的 block id
存到同一个数组,进而在内存维护每个 meta block 的引用状态。
回顾
在了解了宏块、各种宏块类型、SLog 以及从 Meta 和 SLog 恢复内存数据的过程后,将会更容易理解整个 OBServer 的存储结构。整个存储结构从上到下大致可以分为这样几层:
如果您有任何疑问,可以通过以下方式与我们进行交流:
微信群:扫码添加小助手,将拉你进群哟~
钉钉群:33254054