作者简介:镜水,一个致力于无限进步的数据库学徒。
作者简介:海芊,一个致力于当网红的 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 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 的总数据序列化长度 }
MacroBlockMeta
宏块元数据保存了数据宏块中数据列排布、微块索引等重要元数据,从磁盘加载后常驻内存并在内存中维护。
所有宏块元数据(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
。
TableMgrMeta
表管理元数据保存了 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_; }
PartitionMeta
分区元数据由多个 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。
TenantConfigMeta
租户配置元数据由多个 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
。
Super Block
这是第一级 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 的长度 };
TenantConfigMeta
新版本租户配置元数据和旧版本含义一致,解析代码见 ObTenantConfigMetaCheckpointReader::read_checkpoint
。
每个 item 是 TenantUnits
结构,每个 item 也是调用 ObTenantConfigMgr::load_tenant_units
载入。
SuperBlockMeta
这是第一级 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_;
};
MacroBlockMeta
宏块元数据是第二级 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_; };
PGMeta
分区组元数据是第二级 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日志类型。
MacroBlockMeta SLog(main_type = OB_REDO_LOG_MACROBLOCK)
该日志对应于旧版本的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_; };
TableMgr SLog(main_type = OB_REDO_LOG_TABLE_MGR)
该日志对应于旧版本的 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_;
};
TenantConfigMeta Slog (main_type = OB_REDO_LOG_TENANT_CONFIG)
该日志新旧版本都使用。
只有一种日志类型:REDO_LOG_UPDATE_TENANT_CONFIG
。
REDO_LOG_UPDATE_TENANT_CONFIG
:租户配置发生变更。每个 log entry 是 ObUpdateTenantConfigLogEntry
结构,包含 TenantUnits
,replay 时会将原有 tenant_units_
重置并全部更新,意味着即使只有一个租户配置变化,log 里还是保存了所有租户的配置。
struct ObUpdateTenantConfigLogEntry {
share::TenantUnits& units_;
};
PartitionMeta SLog (main_type = OB_REDO_LOG_PARTITION)
该日志新旧版本都使用,并且旧版本的部分日志类型迁移到了该日志下。
由于日志种类比较多,这里只介绍部分日志类型,感兴趣的同学可以进一步阅读代码(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
完成,这里只介绍这一过程,建议参照代码阅读。
ObStoreFile::read_checkpoint_and_replay_log
:
1.根据 super_block
的版本走不同的分支
2.v2 版本(旧版本),先读出 ObSuperBlockV2 super_block
,然后调用 ObServerCheckpointLogReaderV1::read_checkpoint_and_replay_log
3.v3 版本(新版本),直接调用 ObServerCheckpointLogReader::read_checkpoint_and_replay_log
ObServerCheckpointLogReaderV1::read_checkpoint_and_replay_log
:
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 函数。
ObServerCheckpointLogReader::read_checkpoint_and_replay_log
:
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