Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库,以极速易用的特点被人们所熟知,仅需亚秒级响应时间即可返回海量数据下的查询结果,不仅可以支持高并发的点查询场景,也能支持高吞吐的复杂分析场景。基于此,Apache Doris 能够较好的满足报表分析、即席查询、统一数仓构建、数据湖联邦查询加速等使用场景,用户可以在此之上构建用户行为分析、AB 实验平台、日志检索分析、用户画像分析、订单分析等应用。
MPP (Massively Parallel Processing),即大规模并行处理。简单来说,MPP是将任务并行的分散到多个服务器和节点上,在每个节点上计算完成后,将各自部分的结果汇总在一起得到最终的结果(与Hadoop相似)。
Doris整体架构如下图所示,Doris 架构非常简单,只有两类进程
FE 包含三种角色
FE的职责(基于内存,类似HDFS NN)
BE的职责
Doris 的元数据是全内存的。每个 FE 内存中,都维护一个完整的元数据镜像
如上图,Doris 的元数据主要存储4类数据:
Segment整体的文件格式分为数据区域,索引区域和footer三个部分,如下图所示:
DataPage分为两种:nullable和non-nullable的data page。
nullable的data page内容包括:
+----------------+
| value count |
|----------------|
| first row id |
|----------------|
| bitmap length |
|----------------|
| null bitmap |
|----------------|
| data |
|----------------|
| checksum |
+----------------+
non-nullable data page结构如下:
|----------------|
| value count |
|----------------|
| first row id |
|----------------|
| data |
|----------------|
| checksum |
+----------------+
其中各个字段含义如下:
SegmentFooterPB采用了PB格式进行存储,主要包含了列的meta信息、索引的meta信息,Segment的short key索引信息、总行数。
message ColumnPB {
required int32 unique_id = 1; // 这里使用column id, 不使用column name是因为计划支持修改列名
optional string name = 2; // 列的名字, 当name为__DORIS_DELETE_SIGN__, 表示该列为隐藏的删除列
required string type = 3; // 列类型
optional bool is_key = 4; // 是否是主键列
optional string aggregation = 5; // 聚合方式
optional bool is_nullable = 6; // 是否有null
optional bytes default_value = 7; // 默认值
optional int32 precision = 8; // 精度
optional int32 frac = 9;
optional int32 length = 10; // 长度
optional int32 index_length = 11; // 索引长度
optional bool is_bf_column = 12; // 是否有bf词典
optional bool has_bitmap_index = 15 [default=false]; // 是否有bitmap索引
}
// page偏移
message PagePointerPB {
required uint64 offset; // page在文件中的偏移
required uint32 length; // page的大小
}
message MetadataPairPB {
optional string key = 1;
optional bytes value = 2;
}
message ColumnMetaPB {
optional ColumnMessage encoding; // 编码方式
optional PagePointerPB dict_page // 词典page
repeated PagePointerPB bloom_filter_pages; // bloom filter词典信息
optional PagePointerPB ordinal_index_page; // 行号索引数据
optional PagePointerPB page_zone_map_page; // page级别统计信息索引数据
optional PagePointerPB bitmap_index_page; // bitmap索引数据
optional uint64 data_footprint; // 列中索引的大小
optional uint64 index_footprint; // 列中数据的大小
optional uint64 raw_data_footprint; // 原始列数据大小
optional CompressKind compress_kind; // 列的压缩方式
optional ZoneMapPB column_zone_map; //文件级别的过滤条件
repeated MetadataPairPB column_meta_datas;
}
message SegmentFooterPB {
optional uint32 version = 2 [default = 1]; // 用于版本兼容和升级使用
repeated ColumnPB schema = 5; // 列Schema
optional uint64 num_values = 4; // 文件中保存的行数
optional uint64 index_footprint = 7; // 索引大小
optional uint64 data_footprint = 8; // 数据大小
optional uint64 raw_data_footprint = 8; // 原始数据大小
optional CompressKind compress_kind = 9 [default = COMPRESS_LZO]; // 压缩方式
repeated ColumnMetaPB column_metas = 10; // 列元数据
optional PagePointerPB key_index_page; // short key索引page
}
Ordinal Index 索引提供了通过行号来查找 Column Data Page 数据页的物理地址。Ordinal Index 能够将按列存储数据按行对齐,可以理解为一级索引。其他索引查找数据时,都要通过 Ordinal Index 查找数据 Page 的位置。因此,这里先介绍 Ordinal Index 索引。
在一个 segment 中,数据始终按照 key(AGGREGATE KEY、UNIQ KEY 和 DUPLICATE KEY)排序顺序进行存储,即 key 的排序决定了数据存储的物理结构。确定了列数据的物理结构顺序,在写入数据时,Column Data Page 是由 Ordinal index 进行管理,Ordinal index 记录了每个 Column Data Page 的位置 offset、大小 size 和第一个数据项行号信息,即 Ordinal。这样每个列具有按行信息进行快速扫描的能力。Ordinal index 采用的稀疏索引结构,就像是一本书目录,记录了每个章节对应的页码。
Short Key Index 前缀索引,是在 key(AGGREGATE KEY、UNIQ KEY 和 DUPLICATE KEY)排序的基础上,实现的一种根据给定前缀列,快速查询数据的索引方式。这里 Short Key Index 索引也采用了稀疏索引结构,在数据写入过程中,每隔一定行数,会生成一个索引项。这个行数为索引粒度默认为 1024 行,可配置。该过程如下图所示:
在 SegmentFootPB 结构中,每一列索引元数据 ColumnIndexMeta 中存放了当前列的 ZoneMapIndex 索引数据信息。ZoneMapIndex 有两个部分,SegmentZoneMap 和 PageZoneMaps。SegmentZoneMap 存放了当前 Segment 全局的 ZoneMap 索引信息,PageZoneMaps 存放了每个 Data Page 的 ZoneMap 索引信息。
PageZoneMaps 对应了索引数据存放的 Page 信息 IndexedColumnMeta 结构,目前实现上没有进行压缩,编码方式也为 Plain。IndexedColumnMeta 中的 OrdinalIndexPage 指向索引数据 root page 的偏移和大小,这里同样做了优化二级 Page 优化,当仅有一个 DataPage 时,OrdinalIndexMeta 直接指向这个 DataPage;有多个 DataPage 时,OrdinalIndexMeta 先指向 OrdinalIndexPage,OrdinalIndexPage 是一个二级 Page 结构,里面的数据项为索引数据 DataPage 的地址偏移 offset,大小 Size 和 ordinal 信息。
BloomFilterIndex 信息存放了生产的 Hash 策略、Hash 算法和 BloomFilter 过对应的数据 Page 信息。Hash 算法采用了 HASH_MURMUR3,Hash 策略采用了 BlockSplitBloomFilter 分块实现策略,期望的误判率 fpp 默认配置为 0.05。
为了加速数据查询,Apache Doris支持用户为某些字段添加Bitmap索引。Bitmap索引由两部分组成:
例如:如图6所示,一列数据为[x, x, y, y, y, z, y, x, z,
x],一共包含10行,则该列数据的Bitmap索引的有序字典为{x, y, z}, x、y、z对应的位图分别为:x的位图: [0, 1, 7, 9] ( [1,1,0,0,0,0,0,1,0,1,0,0…] )
y的位图: [2, 3, 4, 6]
z的位图: [5, 8]
在查询一个 Segment 中的数据时,根据执行的查询条件,会对首先根据字段加索引的情况对数据进行过滤。然后在进行读取数据,整体的查询流程如下:
大体的写入流程如下:
相关的问题:
1.https://doris.apache.org/zh-CN/community/design/metadata-design
2.https://blog.csdn.net/qq_43141726/article/details/120607561
3.https://xie.infoq.cn/article/4f7d09d6185fb3055d4e7e51c
4.https://www.modb.pro/db/427508