MatrixOne Layout 设计解读

数据库
SQL
云原生
0
0
<!--StartFragment--> **作者:申江伟 MatrixOne 研发工程师** **MatrixOne从0.5设计开始就已经确定采用列存结构来存储数据集,原因如下:** 1. 很容易对AP优化; 2. 通过引入Column Family的概念可以对负载灵活适配。假如所有列都是一个Column Family,也就是所有的列数据保存在一起,这就跟数据库的HEAP文件非常类似,可以表现出行存类似的行为,典型的OLTP数据库如PostgreSQL就是基于HEAP来做的存储引擎。假如每个列都是独立的Column Family,也就是每一列都独立存放,那么就是典型的列存。通过定义Column Family,用户可以方便地在行存和列存之间切换,这只需要在DDL表定义中指定即可。 *** ## **Part 1 Layout需要满足的条件及解决的问题** 设计一种Layout,首先要提出一个疑问。 ***我们到底需要满足什么功能和需求?*** 对于MatrixOne来说,需要存储的数据类型有很多种,比如:数据库表数据、元数据、数据库业务trace log、查询结果的cache... ... **功能1:支持存储MatrixOne所有的业务类型数据。** MatrixOne需对接S3或共享对象存储,每一个对象需要存储指定行数或Size的存储集,所以一个对象会存储多个数据单元,对于每一个数据单元我们称之为Block。这些Block需要高效读取和管理,比如一个查询需要读取哪些Block,首先需要拿到Block的元数据并分析,然后再根据这些Block的元数据得到真正数据的存放地址并读取。 **功能2:便捷高效的元数据。** 当MatrixOne运行一段时间,客户需求不断增加,这时不得不修改当前的Layout。 **功能3:支持数据的版本兼容和控制。** 支持数据分析工具和添加Scrub任务。 **功能4:支持从数据对象文件中重建MatrixOne的表结构。** <!--EndFragment--> ![640.jpg](https://dev-media.amazoncloud.cn/9d77527bd5f54c979dfb4ffc0b2eea75_640.jpg "640.jpg") <!--StartFragment--> 根据上诉几个问题,我们设计了现在的Layout,它由Header、数据区、索引区、元数据区和Footer组成。这些区域的作用如下: Header :记录了当前Layout的版本、元数据区的位置等信息。 数据区 :存储了数据对象的数据信息。 索引区 :存储了数据对象的索引信息。 元数据区 :存储了数据对象的元数据信息,这些元数据包括数据对象的类型、数据对象的大小、行数、列数、压缩算法等信息。 Footer :可以看作是一个Header的镜像。 *** ## **Part 2 结构解析** ### **1 *Extent*** 在MatrixOne中,Extent负责记录一个IOEntry的位置信息。 ```text +------------+----------+-------------+-------------+ | Offset(4B) | Size(4B) | OSize (4B) | Algo (1B) | +------------+----------+-------------+-------------+ Offset = IOEntry的起始地址 Size = IOEntry存储在对象中的大小 oSize = IOEntry压缩前的原始大小 Algo = 压缩算法类型 ``` ### **2 *ObjectMeta*** <!--EndFragment--> ![640.jpg](https://dev-media.amazoncloud.cn/843d9433e4854089a4a6af377866202e_640.jpg "640.jpg") <!--StartFragment--> 为什么我们先介绍ObjectMeta而不是Header,因为MatrixOne的查询业务是从ObjectMeta开始的。 MatrixOne向S3写入数据成功后,会返回一个记录ObjectMeta位置的Extent,并保存到Catalog中。 当MatrixOne执行查询操作需要读取数据时,可以通过这个Extent拿到ObjectMeta,从而拿到Block的真实数据。 ObjectMeta由多个BlockMeta、一个MetaHeader、一个BlockIndex组成。 MetaHeader和BlockMeta结构一致,用来记录整个Object的全局信息,比如dbID,TableID,ObjectName和Object全局数据的的ZoneMap、Ndv、nullCut等等。 BlockIndex记录后面每一个BlockMeta的地址。ObjectMeta在MatrixOne系统中使用非常频繁, 虽然每一个BlockMeta的地址可以遍历算出来,但是为了节省开销和提高性能,还是记录了BlockIndex。 BlockMeta记录了每一个Block的元数据信息。 ### **3 *BlockMeta & MetaHeader*** BlockMeta由一个Header和多个ColumnMeta组成。 Header记录了BlockID、Block的Rows、Column Count等信息。 ColumnMeta记录了每一列的数据地址、Null Count(当前列Null值的数量)、Ndv(当前列有多少不同的值)等信息。 **>>> Block Meta Header** <!--EndFragment--> <!--StartFragment--> ```text +----------------------------------------------------------------------------------------------------+ | Header | +----------+------------+-------------+-------------+------------+--------------+--------------------+ | DBID(8B) | TableID(8B)|AccountID(4B)|BlockID(20B) | Rows(4B) | ColumnCnt(2B)| BloomFilter(13B) | +----------+------------+-------------+-------------+------------+--------------+--------------------+ | Reserved(37B) | +----------------------------------------------------------------------------------------------------+ | ColumnMeta | +----------------------------------------------------------------------------------------------------+ | ColumnMeta | +----------------------------------------------------------------------------------------------------+ | ColumnMeta | +----------------------------------------------------------------------------------------------------+ | .......... | +----------------------------------------------------------------------------------------------------+ DBID = Database id TableID = Table id AccountID = Account id BlockID = Block id。 Rows = MetaHeader中为对象的行数,BlockMeta中为当前Block的行数 ColumnCnt = 在对象或Block中有多少列 BloomFilter = 存储BloomFilter区域的地址,只在MetaHeader中有效 ``` **>>> Column Meta** ```text +--------------------------------------------------------------------------------+ | ColumnMeta | +--------+---------+-------+-----------+---------------+-----------+-------------+ |Idx(2B) |Type(1B) |Ndv(4B)|NullCnt(4B)|DataExtent(13B)|Chksum(4B) |ZoneMap(64B) | +--------+---------+-------+-----------+---------------+-----------+-------------+ | Reserved(32B) | +--------------------------------------------------------------------------------+ Idx = Column的序号 Ndv = Column中有多少不同的值 NullCnt = Column中有多少Null值 DataExtent = Column数据的位置 Chksum = Column数据的checksum ZoneMap = Column的ZoneMap,大小固定为64B ``` **>>> Header** Header和Footer记录信息一致,只不过位置不同。 ```text +---------+------------+---------------+----------+ |Magic(8B)| Version(2B)|MetaExtent(13B)|Chksum(4B)| +---------+------------+---------------+----------+ Magic = Engine identity (0x0xFFFFFFFF) Version = 对象文件的版本号 MetaExtent = ObjectMeta的位置信息 Chksum = ObjectMeta的checksum ``` **>>> Footer** ```text +----------+----------------+-----------+----------+ |Chksum(4B)| MetaExtent(13B)|Version(2B)| Magic(8B)| +----------+----------------+-----------+----------+ ``` *** ## **Part 3 通过Extent读数据** 在介绍ObjectMeta时,我们知道,MatrixOne向S3写入数据成功后, 会返回一个记录ObjectMeta位置的Extent。这里时候我们会通过Extent来执行读数据的操作。 首先,通过Extent中的地址,向S3请求读一个IO Entry,并且放入MatrixOne的cache中。 这个IO Entry就是ObjectMeta的全部内容。通过位移计算可以拿到BlockIndex,根据BlockIndex记录的Extent可以得到被读的 BlockMeta,到此为止我们元数据操作已经结束,剩下就是向S3请求读一个个Column Data了。 ```text +-----------+ | Extent | +-----------+ | | +-------------------------+ | IO Entry | +-------------------------+ | ObjectMeta | +-------------------------+ | | +--------------------------------------------------------------------- | BlockIndex | +--------+------------+------------+------------+-----------+--------+ | Count | <Extent-1> | <Extent-2> | <Extent-3> | Extent-4> | ...... | +--------+------------+------------+------------+-----------+--------+ | | | | +------------------------------------------------+ | | Block1(BlockMeta) | | +--------------+--------------+--------------+---+ | |<ColumnMeta-1>|<ColumnMeta-2>|<ColumnMeta-3>|...| | +--------------+--------------+--------------+---+ | | | | | | | | +------------------+ +----------+ +----------+ +----------+ | Block4(BlockMeta)| | IO Entry | | IO Entry | | IO Entry | +------------------+ +----------+ +----------+ +----------+ | <ColumnMeta>... | |ColumnData| |ColumnData| |ColumnData| +------------------+ +----------+ +----------+ +----------+ | | +------------------+ | IO Entry... | +------------------+ ``` *** ## **Part 4 版本兼容** ### **>>> IOEntry** IOEntry表示一个IO单元,具体对应在Layout中就是:ObjectMeta、每一个Column的数据、BloomFilter区还有Header和Footer。 除了Header和Footer,我们需要在每一个IOEntry头添加两个flag:Type\&Version。 每一个结构或模块需要实现Encode/Decode函数,然后注册到MatrixOne中,MatrixOne读出IOEntry后会根据这两个flag来选择对应的代码。 ```go const ( IOET_ObjectMeta_V1 = 1 IOET_ColumnData_V1 = 1 IOET_BloomFilter_V1 = 1 ... IOET_ObjectMeta_CurrVer = IOET_ObjectMeta_V1 IOET_ColumnData_CurrVer = IOET_ColumnData_V1 IOET_BloomFilter_CurrVer = IOET_BloomFilter_V1 ) const ( IOET_Empty = 0 IOET_ObjMeta = 1 IOET_ColData = 2 IOET_BF = 3 ... ) ``` 以ObjectMeta为例。我们需注册V1的Encode/Decode函数代码,设置IOET_ObjectMeta_CurrVer为V1,并写入数据。 ```go const ( IOET_ObjectMeta_V1 = 1 IOET_ObjectMeta_V2 = 2 ... IOET_ObjectMeta_CurrVer = IOET_ObjectMeta_V2 ... ) func EncodeObjectMetaV2(meta *ObjectMeta) []byte { ... } func DecodeObjectMetaV2(buf []byte) *ObjectMeta { ... } RegisterIOEnrtyCodec(IOET_ObjMeta,IOET_ObjectMeta_V2,EncodeObjectMetaV2,DecodeObjectMetaV2) ObjectMeta.Write(IOET_ObjMeta, IOET_ObjectMeta_CurrVer) ``` *** > MatrixOrigin 官网:[新一代超融合异构开源数据库-矩阵起源(深圳)信息科技有限公司 MatrixOne](https://link.zhihu.com/?target=https%3A//matrixorigin.cn/) <!--EndFragment-->
目录
亚马逊云科技解决方案 基于行业客户应用场景及技术领域的解决方案
联系亚马逊云科技专家
亚马逊云科技解决方案
基于行业客户应用场景及技术领域的解决方案
联系专家
0
目录
关闭