一文读懂 OceanBase 数据库的启动恢复代码解析

2024年 5月 7日 34.7k 0

作者简介:镜水,一个致力于无限进步的数据库学徒。

作者简介:海芊,一个致力于当网红的 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
    };
  };
};

NOTEprevious_block_index_ 为前向指针,通常为链式结构的元数据宏块,它表明从新到旧的一个前向关系。例如,下表所示宏块链表的写入顺序为 12345,但由于前向链接,读取时的入口点为 5,从而导致读取顺序为 54321,因此实际使用数据时,通常需要一次正向(54321)读取获取 block id 的数组后,再进行一次反向(12345)读取,从而得到正确的数据写入顺序。

一文读懂 OceanBase 数据库的启动恢复代码解析-1

在了解了数据块的基本单元 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 Blockmeta block,其中每个 tenantSuper Block(第二级 Super Block)是一个 SuperBlockMeta(第一级 meta)item,和第一级 Super Block 在同一个文件,而每个 tenantmeta 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 结构,包含了一个 ObTenantFileInfoObTenantFileInfo 包含了一个 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_SSTABLEREDO_LOG_COMPELTE_SSTABLEREDO_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 结构,包含 ObPGKeyObSSTable。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 blockmacro_meta/table_mgr_meta/partition_meta/tenant_config_meta),并使用对应元数据的类解析函数 parse 出相应的信息恢复到内存(比如 partition_meta 能够解析出每个分区的基础信息以及包含的 sstable 信息,将以某种结构保存到内存),然后将所有 meta blockblock 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 读取所有 tenantsuper block meta(这些 super block meta 相当于第一级 meta,第二级的 super block,和 super_block 在同一个文件上,入口点在 super_block上 ,也是多个 macro block 前向链接而成):首先ObPGMetaBlockReader::initsuper block meta 入口点读取所有的super block metaObPGMetaBlockReader::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::initsuper block meta 入口点读取所有的 super block metaObPGMetaBlockReader::get_meta_blocks),并反向预取出第一个 meta block 的数据保存在内存,接着将反向读取每一个 meta block;然后依次解析每个 meta item ,不断重新覆盖 tenant configObTenantConfigMgr::load_tenant_units)

5.ObServerCheckpointLogReader::replay_server_slog回放第一级 meta(tenant 的 super block metatenant 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:首先从 tenentfile_info 能够得到 tenant_file_super_block_,从而获得 macro metapg 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 blockblock id 存到同一个数组,进而在内存维护每个 meta block 的引用状态。

回顾

在了解了宏块、各种宏块类型、SLog 以及从 Meta 和 SLog 恢复内存数据的过程后,将会更容易理解整个 OBServer 的存储结构。整个存储结构从上到下大致可以分为这样几层:

一文读懂 OceanBase 数据库的启动恢复代码解析-1

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

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

一文读懂 OceanBase 数据库的启动恢复代码解析-3

钉钉群:33254054

一文读懂 OceanBase 数据库的启动恢复代码解析-4

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论