Doris存储层设计介绍3——读取流程、Compaction流程分析

目录

一、概述

1.1 存储结构的整体介绍

二、读取流程

2.1 整体读取流程

2.2 读取init阶段的主要流程

2.2.1 OlapScanner 查询参数构造

2.2.2 Reader的Init流程

2.2.3 RowsetReader的Init流程

2.2.4 Segmentlterator的Init流程

2.3 读取next阶段的主要流程

2.3.1 Reader读取next_row_with_aggregation

2.3.2 Collectlterator读取next

2.3.3 RowsetReader读取next

2.3.4 Segmentlterator读取next_batch 

三、Compaction流程

3.1 Compaction整体介绍

3.1.1 Compaction概述

3.1.2 Rowset数据版本

 3.1.3 Compaction执行方式

3.2 Compaction详细流程

四、总结

一、概述

1.1 存储结构的整体介绍

    Doris是基于MPP架构的交互式SQL数据仓库,主要用于解决近实时的报表统计和多维分析。Doris高效导入、查询离不开其存储结构的设计。通过阅读Doris BE模块代码,分析Doris BE模块存储层的实现原理,主要包括Doris列存的设计、索引设计、数据读写流程、Compaction流程、Tablet数据分片和Rowset版本管理等功能。通过三篇文章来逐步进行阐述,分别为:

Doris存储层设计介绍1——存储结构设计解析(索引底层结构)-CSDN博客文章浏览阅读1.1k次,点赞33次,收藏19次。Doris存储层设计介绍1——存储结构设计解析(索引底层结构)https://blog.csdn.net/SHWAITME/article/details/136155008?spm=1001.2014.3001.5502

Doris存储层设计介绍2——数据写入及删除流程-CSDN博客文章浏览阅读66次。Doris存储层设计介绍2——数据写入及删除流程https://blog.csdn.net/SHWAITME/article/details/136156144?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22136156144%22%2C%22source%22%3A%22SHWAITME%22%7D

Doris存储层设计介绍3——读取流程、Compaction流程分析-CSDN博客文章浏览阅读2次。Doris存储层设计介绍3——读取流程、Compaction流程分析https://blog.csdn.net/SHWAITME/article/details/136223060?spm=1001.2014.3001.5501

   本文章介绍了Doris读取流程及Compaction流程

二、读取流程

2.1 整体读取流程

     读取流程为写入的逆过程,读取流程相对复杂些,主要因为进行大量的读取优化。整个读取流程分为两个阶段,一个是init流程,一个是获取next_block数据块的过程。具体过程如下图所示:

 层级关系如下:

  1. OlapScanner对—个tablet数据读取操作整体的封装;
  2. Reader对读取的参数进行处理,并提供了按三种不同模型读取的差异化处理;
  3. CollectIterator包含了tablet中多个RowsetReader,这些RowsetReader有版本顺序,CollectIterator将这些RowsetReader归并Merge成统一的lterator功能,提供了归并的比较器;
  4. RowsetReader则负责了对一个Rowset的读取;
  5. RowwiseIterator提供了一个Rowset中所有Segment的统一访问的lterator功能。这里的归并策略可以根据数据排序的情况采用Merge或Union;
  6. Segmentlterator对应了一个Segment的数据读取,Segment的读取会根据查询条件与索引进行计算,找到读取的对应行号信息,定位到对应的数据页page,对数据进行读取。其中,经过过滤条件后,会对可访问的行信息生成bitmap来记录,BitmapRangelterator代表单独实现的,可以按照范围访问这个bitmap的迭代器;
  7. Columnlterator提供了对列的相关数据和索引统一访问的迭代器。ColumnReader以及各个IndexReader等分别对应具体的数据和索引信息。

2.2 读取init阶段的主要流程

  init阶段的执行流程如下:

2.2.1 OlapScanner 查询参数构造

  1. 根据查询指定的version版本查找出需要读取的RowsetReader(依赖于版本管理的rowset_graph版本路径图,取得查询version范围的最短路径);
  2. 设置查询信息,包括_tablet、读取类型reader_type=READER_QUERY、是否进行聚合、_version (从o到指定版本);
  3. 设置查询条件信息,包括filter过滤字段、is_nulls字段;
  4. 设置返回列信息;
  5. 设置查询的key_ranges范围(key的范围数组,可以通过short key index前缀索引进行过滤);
  6. 初始化Reader对象;

2.2.2 Reader的Init流程

  1. 初始化conditions查询条件对象;
  2. 初始化bloomFilter列集合(eq、in条件,添加了bloomFilter的列);
  3. 初始化delete_handler,包括了tablet中存在的所有删除信息,其中包括了版本和对应的删除条件数组;
  4. 初始化传递给下层要读取返回的列,包括了返回值和条件对象中的列;
  5. 初始化key_ranges的start key、end key对应的RowCusor行游标对象等;
  6. 构建的信息设置RowsetReader、Collectlterator。Rowset对象进行初始化,将RowsetReader加入到Collectterator中;
  7. 调用Collectlterator获取当前行(这里其实为第一行),这里开启读取流程;

2.2.3 RowsetReader的Init流程

  1. 构建Segmentlterator并过滤掉delete_handler中比当前Rowset版本小的删除条件;
  2. 构建Rowwiselterator (是对SegmentIterator的聚合iterator),将要读取的Segmentlterator加入到Rowwiselterator。当所有的Segment为整体有序时,采用union iterator顺序读取的方式,否则采用merge iterator归并读取的方式。

2.2.4 Segmentlterator的Init流程

  1. 初始化ReadableBlock,用来读取当前的Segment文件的对象,实际读取文件;
  2. 初始化_row_bitmap,用来存储通过索引过滤后的行号,使用bitmap结构;
  3. 构建Columnlterator,这里仅是需要读取列;
  4. 如果Column有BitmapIndex索引,初始化每个Column的Bitmaplndexlterator;
  5. 通过SortkeyIndex前缀索引过滤数据;
  6. 借助各种索引,按条件过滤数据,条件包括查询条件和删除条件过滤信息。

2.3 读取next阶段的主要流程

  next阶段的执行流程如下:

2.3.1 Reader读取next_row_with_aggregation

    在reader读取时预先读取一行,记录为当前行。在被调用next返回结果时会返回当前行,然后再预先读取下一行作为新的当前行,reader的读取会根据模型的类型分为三种情况:

  1. _dup_key_next_row读取(明细数据模型)下,返回当前行,再直接读取Collectorlterator读取next作为当前行;
  2. agg_key_next_row读取(聚合模型)下,取Collectorlterator读取next之后,判断下一行是否与当前行的key相同,相同时则进行聚合计算,循环读取下一行;不相同则返回当前累计的聚合结果,更新当前行;
  3. _unique_key_next_row读取(unique key模型)下,与_agg_key_next_row模型方式逻辑相同,但存在一些差异。由于支持了删除操作,会查看聚合后的当前行是否标记为删除行。如果为删除行舍弃数据,直到找到一个不为删除行的数据才进行返回。

2.3.2 Collectlterator读取next

   Collectlterator中使用heap(堆)数据结构维护了要读取RowsetReader集合,比较规则为:key相同时比较Rowset的版本。

2.3.3 RowsetReader读取next

  1. RowsetReader直接读取了Rowwiselterator的next_batch;
  2. Rowwiseterator整合了Segmenterator。当Rowset中所有Segment整体有序时直接按Union方式迭代返回,无序时按Merge归并方式返回。Rowwiselterator返回当前最大的SegmentIterator的行数据,每次会调用SegmentIterator的next_batch获取数据。

2.3.4 Segmentlterator读取next_batch 

  1. 根据init阶段构造的BitmapRangerInterator,使用next_range每次取出要读取的行号的一个范围range_from、 range_to;
  2. 先读取条件列从range_from到range_to行的数据。过程如下:
  • 调用有的条件列的各个columnlterator的seek_to_ordinal,各个列的读取位置current_rowid定位到Segmentlterator的cur_rowid。这里是通过二分查ordinal_index(一级索引)对齐到对应的data page(数据页)。
  • 读出条件列的数据,按条件再进行一次过滤(这次是精确的过滤)。
  • 再读取无条件列的数据,放入到Rowblock中,返回Rowblock。

三、Compaction流程

3.1 Compaction整体介绍

3.1.1 Compaction概述

     Doris 通过类似 LSM-Tree 的结构写入数据,后台通过 Compaction机制不断将小文件合并成有序的大文件。对于单一的数据分片(tablet),数据会按照顺序写入内存(写缓存memstore),达到阈值后刷写到磁盘,这些文件保存在一个rowset中。在Doris中,Compaction机制根据一定的策略对这些rowset合并成有序的大文件,极大地提升查询性能。

3.1.2 Rowset数据版本

     Doris 通过类似 LSM-Tree 的结构写入数据,后台通过 Compaction机制不断将小文件合并成有序的大文件。对于单一的数据分片(tablet),数据会按照顺序写入内存(写缓存memstore),达到阈值后刷写到磁盘,这些文件保存在一个rowset中。在Doris中,Compaction机制根据一定的策略对这些rowset合并成有序的大文件,极大地提升查询性能。

      一个tablet中包含若干连续的rowset(rowset是逻辑概念),rowset代表tablet中一次数据变更的数据集合(数据变更包括了数据新增,更新或删除等)。rowset按版本信息进行记录,版本信息中包含了两个字段first和second,first表示当前rowset的起始版本(start version),end表示当前rowset的结束版本(endversion)。

    Doris的数据写入是以微批的方式进行的,每一个批次的数据针对每个tablet都会形成一个rowset(一个tablet是由多个rowset组成的)。每个rowset都有一个相应的起始版本(start version)和终止版本(end version)。对于新增的rowset,起始版本和终止版本是一样的,表示为[ 6-6]、[ 7-7]等。多个 rowset经过compaction会形成一个大的rowset。合并后的起始版本和终止版本是多个版本的并集,如[ 6-6]、[ 7-7]、[8-8]合并后变成 [6-8],如下图:

 3.1.3 Compaction执行方式

    Compaction执行方式可以分为两种类型:base compaction和cumulative compaction,其中cumulative compaction(简称CC)负责将多个最新导入的增量数据进行合并,当增量数据合并后的大小达到一定阈值后,base compaction(简称BC)将基线版本(起始版本start version为0的数据)和与该增量数据版本合并。BC操作因为涉及到基线数据,而基线数据通常比较大,所以操作耗时会比CC长。

    这两种compaction的边界通过cumulative  point (简称CP)来确定。CP是一个动态变化的版本号,比CP小的数据版本只能触发BC,而比CP大的数据版本,只会触发CC。

3.2 Compaction详细流程

  Compaction合并整体流程如下图所示:

(1)计算cumulative_point;

(2)选择compaction需要合并的InputRowsets集合:

  • base compaction选取条件:(1)如果存在大于5个非cumulative的rowset,将所有非cumulative的rowset进行合并;(2)版本first为0的base rowset与其他非cumulative的磁盘比例小于10:3时,合并所有非cumulative的rowset进行合并;
  • cumulative compaction选取条件:(1)当Rowset集合中segment数量>=5且<=1000时,进行合并;(2)当输出Rowset数量小于5,但存在删除条件版本大于Rowset second版本时,进行合并(让删除的Rowset快速合并进来);(3)当累计的base compaction和cumulative compaction都时间大于1天时,进行合并;

(3)执行compaction;

(4)更新cumulative_point:

  • 更新cumulative_point,将cumulative compaction产出的OutputRowset交给后续的base compaction流程。

    ps:对于aggregation key聚合模型、unique key主键模型而言,compaction功能可以使得分散在不同Rowset,而key值相同的数据进行合并,达到了预计算的效果。此外,开启compaction后减少了Rowset文件数量,从而提升了查询效率。

    ps:compaction合并机制的详细内容见文章:

第3.2章:Doris数据导入——Compaction机制-CSDN博客文章浏览阅读976次,点赞17次,收藏21次。第3.2章:Doris数据导入(Doris 1.2.2版本之前)——Compaction机制https://blog.csdn.net/SHWAITME/article/details/136172846?spm=1001.2014.3001.5501

四、总结

   本文详细介绍了Doris系统的底层存储层的读取相关流程。

   读取流程依赖于完全的列存实现,对于OLAP的宽表场景(读取大量行,少量列)能够快速扫描, 基于多种索引功能进行过滤(包括Short Key Index前缀索引Ordinal Index一级索引、ZoneMap索引、 Bitmap位图索引、BloomFilter 索引等),能够跳过大量的数据扫描。此外,Compaction机制根据一定的策略对多个小rowset合并成有序的大文件,极大地提升查询性能。

参考文章:

【Doris】存储层设计介绍3——读取流程、Compaction流程分析_doris 存储层设计介绍 3—— 读取流程、compaction 分析-CSDN博客

  • 36
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值