作者:
公祺,一个专注于 OBKV 的程序员
1.宏块的概述
在上一篇微块的存储格式中已经介绍了微块和宏块的关系,宏块是处于 SSTable 和微块之间的数据结构,OceanBase 中的宏块为2MB的定长数据块。众所周知,OceanBase 中微块是读 IO 最小单元,这是因为微块读处在用户请求的关键路径上,为保证快速响应用户的请求,微块不能过大,所以微块的默认大小一般不超过16KB;而宏块作为写 IO 的最小单元,它的读写不在用户请求的关键路径上,所以就有了2MB的宏块,目的是为了最大限度的发挥磁盘的吞吐性能,能快速的做 compaction 、迁移复制、坏块检查等操作。宏块的简单结构可以参考下图,详细的宏块格式介绍见下一节:
注:本文所有的说明及代码都是基于v3.1.0_CE_BP1版本的 OceanBase 开源代码。
2. 宏块的格式
目前 OceanBase 支持的宏块有很多种,具体可以 enum MacroBlockType 的定义,总共有十几种吧,但是常用的数据宏块主要有三种,如下:
- SSTableData:常规的存放数据的宏块;
- LobData:Large Object Data,用来存放数据较大的行数据;
- BloomFilterData:带有 bloomfilter 的宏块。
本文主要介绍第1种常规的数据宏块,关于 LobData、BloomFilterData,后面找时间再单独说明。
宏块的整体格式可以参考上图,它是一种比较经典的存储结构(header + payload + trailer + padding):
- header 中记录元数据:对应 OceanBase 宏块的 header ;
- payload 存放的是具体的数据:OceanBase 宏块的 payload 为微块列表;
- trailer 中记录的是数据的 index:OceanBase 宏块的 trailer 为微块的 index 信息,即为微块在宏块中的偏移量;
- padding 是为了做对齐的:OceanBase 宏块为2MB,不足部分需要做 padding。
后面我们针对不同的部分,一一介绍其结构的存储格式。
2.1 宏块的 header
宏块的头部记录的自然就是宏块的元数据,它由多个部分组成,如下图所示:
宏块头部的各个部分存储的是不同的元数据,具体含义如下:
- common header:宏块的版本、类型、大小、checksum 等信息,见 ObMacroBlockCommonHeader ;
- macro block header:记录了宏块数据大小、table_id、partition_id、微块的数量、列数、行数、checksum、加密信息、以及相关 offset 信息,具体可以参考 struct ObSSTableMacroBlockHeader;
- column id list:列的 id 列表,OceanBase 数据库表每一列都一个唯一 id;
- column type list:每列的类型信息,包括:类型、编码字符集等;
- column order list:每列的顺序,可以是 ASC 或 DESC ,宏块中所有微块中的行数据都是按照这个顺序存储;
- column checksum list:每列数据的 checksum 信息,用来做列的数据校验。
微块头部的存储格式可以参考下面的代码:
// src/storage/blocksstable/ob_macro_block.cpp // 该函数主要给宏块header结构预先指向buffer的不同的offset,后续就不需要再进行序列化操作了, // 该函数在初始化宏块的时候调用,header成员变量的具体值是在后续的数据写入后指定。 int ObMacroBlock::reserve_header(const ObDataStoreDesc& spec) { int ret = OB_SUCCESS; common_header_.reset(); common_header_.set_attr(ObMacroBlockCommonHeader::SSTableData); common_header_.set_data_version(spec.data_version_); common_header_.set_reserved(0); const int64_t common_header_size = common_header_.get_serialize_size(); // data_的类型是ObSelfBufferWriter,它是一个支持自动扩展的内存buffer // ObSelfBufferWriter的实现见:src/storage/blocksstable/ob_data_buffer.h MEMSET(data_.data(), 0, data_.capacity()); // 1. data_的第一部分为ObMacroBlockCommonHeader if (OB_FAIL(data_.advance(common_header_size))) { STORAGE_LOG(WARN, "data buffer is not enough for common header.", K(ret), K(common_header_size)); } if (OB_SUCC(ret)) { int64_t column_count = spec.row_column_count_; int64_t rowkey_column_count = spec.rowkey_column_count_; int64_t column_checksum_size = sizeof(int64_t) * column_count; int64_t column_id_size = sizeof(uint16_t) * column_count; int64_t column_type_size = sizeof(ObObjMeta) * column_count; int64_t column_ord