【StoneDB join 算法分析】查询模块

1. 背景介绍

StoneDB 采用基于知识网格技术和列式存储引擎。该存储引擎为海量数据背景下 OLAP 应用而设计,通过列式存储数据、知识网格过滤、高效数据压缩等特性,为应用系统提供低成本和高性能的数据查询支持。
本文旨在分析 StoneDB 引擎的查询模块,包括:查询模块结构图、查询流程、知识网格、知识网格优化、优化算法。

2. StoneDB架构图

【StoneDB join 算法分析】查询模块_第1张图片

3. 查询模块结构图

【StoneDB join 算法分析】查询模块_第2张图片

SQL模块(Query Layer)

1.SQL Parser

解析器的主要作用是解析SQL语句,最终生成语法树。解析器会对SQL语句进行语法和语义分析,如果有错误,则返回相应的错误信息。检查通过后,解析器会查询缓存,如果缓存中有对应的语句和查询结果,则直接返回结果,不进行接下来的优化和执行步骤。

2.Optimizer

优化器的主要作用是对SQL语句进行优化,包括选择合适的索引,数据的读取方式,生成代价最小的执行计划。

3.Execute

执行器的主要作用是判断操作的表是否有权限,如果有权限,会根据表的存储引擎定义,调用接口去读取数据,最后返回满足条件的查询结果。

4.Filter

粗糙集过滤,找到可能包含的包。

5.DPN evaluation

根据DPN迭代判断包里面每条是否满足。

6.decompress

分片并行解压粗糙过滤后命中的包。

知识网格模块(Knowledge Grid)

知识网格是由数据包节点和知识节点组成的。由于数据包都是压缩存放的,所以数据读取解压的代价比较高,在查询中如何做到读取尽量少的数据包是提升效率的关键。知识网格正是起到了这样的一个作用,它能够有效的过滤查询中不符合条件的数据,以最小的代价定位以数据包为最小单位的数据。知识网格的数据大小只占数据总量的1%以下,通常情况下可以加载到内存中,进一步提升查询效率。

对于大部分统计、聚合性查询,StoneDB 往往只需要使用知识网格就能返回查询结果,这是因为通过知识网格可以消除或大量减少需要解压的数据包,降低 IO 消耗,提高查询响应时间和网络利用率。例如:数据包节点记录了最大值、最小值、平均值、总和、总记录数、null 值的数量,如果想对某个列做聚合运算,那么知识网格就能根据这些元数据很快的得到结果,而无需再解压访问底层的数据包。

1.数据包节点(Data Pack Node)

数据包节点也称为元数据节点(Metadata Node),因为数据包节点记录了每个数据包中列的最大值、最小值、平均值、总和、总记录数、null 值的数量、压缩方式、占用的字节数。每一个数据包节点对应一个数据包。

2.知识节点(Knowledge Node)

数据包节点的上一层是知识节点,记录了数据包之间或者列之间关系的元数据集合,比如值数据包的最小值与最大值的范围、列之间的关联关系。大部分的知识节点数据是装载数据的时候产生的,另外一部分是查询的时候产生的。

4. 查询流程图

【StoneDB join 算法分析】查询模块_第3张图片
查询流程大致步骤如下:

1.Client 连接

与数据库建立连接,此过程遵循 MySQL5.6、5.7 的连接协议。

2.SQL Parser

对 SQL 语句进行语法和语义分析。

解析入口:

parse_sql()

3.StoneDB Optimizer

对 SQL 语句进行优化,生成代价最小的执行计划。

优化函数:

optimize_select()

4.知识网格

StoneDB 在执行查询的时候会根据知识网络(Knowledge Grid)把 DP 分成三类:

  • 相关的 DP(Relevant Packs),满足查询条件限制的 DP
  • 不相关的 DP(Irrelevant Packs),不满足查询条件限制的 DP
  • 可疑的 DP(Suspect Packs),DP 里面的数据部分满足查询条件的限制

入口函数:

RCAttr::ApproxAnswerSize

获取DN:

Pack *get_pack(size_t i)

5.命中之后,解压相关包。

主函数:

CprsErr Decompress

6.返回结果集。

主函数:

ResultSender

5. 知识网格

5.1 知识网络总览图

【StoneDB join 算法分析】查询模块_第4张图片

     知识网格由数据元信息节点 (MD) 和知识节点 (KN) 组成, 通过知识网格,StoneDB 引擎无需通过传统数据索引、数据分区、预测、手动调优或特定架构等方式就能高速处理复杂的分析查询。  

5.2 元信息节点(MD)

【StoneDB join 算法分析】查询模块_第5张图片
数据元信息节点 (MD) 与数据节点 (DN) 之间保持 1:1 关系,元信息节点中包含了其对应数据块中数据的相关信息:

  • 数据聚合函数值 (MIN,MAX,SUM,AVG 等)。
  • 记录数量 (count)。
  • 数据中的 null 记录标记。
  • 元信息节点在数据装载的时候就会构建,MD 为数据压缩, 聚合函数即席查询等技术提供了支持。
  • 知识节点 (KN) 除了基础元数据外,还包括数据特征以及更深度的数据统信息。
  • 知识网格存储在内存中,方便快速查询。

数据结构:

struct DPN{}

获取DPN:

DPN &get_dpn

5.3 知识节点(KN)

【StoneDB join 算法分析】查询模块_第6张图片

  • Column Metadata 包含了该列的数据类型定义,约束条件等基础信息。

主类:

class impl
  • FieldRange 是一个标识数据节点 (DN) 中记录值范围段的标识 Map。针对数值类型的记录 (date/time, integer,decimal…),FieldRange 可以用来快速确认当前对应 DN 是否包含所需数据。FieldRange 的组成:

    • 从 MD 中获取数据块的 MAX 与 MIN,并将 MAX-MIN 划分为固定段(例如1024)。
    • 每个段中分别使用1个 bit 标识是否有记录存在与该范围内。

file

  • Char Map 是一个记录字符是否匹配的 Map。 针对字符类型的记录(char,varchar等),CharMap 可以快速确定当前 DP 是否包含所需数据。

    • 统计当前 DN 内,1-64 长度中 ASCII 字符是否存在的标识值。
    • 字符检索时,按照字符顺序,依次对比字符标示值即可知道该 DN 是否包含匹配数据。

【StoneDB join 算法分析】查询模块_第7张图片

  • join nodes

【StoneDB join 算法分析】查询模块_第8张图片

  • 在 Join 查询时自动创建,以关系位图的形式,存储 Join 操作中关联 DataNode 的信息。
  • 存储在 Session 中,仅对当前 Client 生效。
  • 显著提高Join查询性能。减少无效 DN 扫描,避免无用 IO 的产生。
  • distributions

    • 统计当前 Column 中各记录的值分布信息。
    • 基于统计信息,和粗糙集 (RoughSet) 计算,提供近似查询支持 (Approximate Query)。

【StoneDB join 算法分析】查询模块_第9张图片

6 知识网格优化器

对于来自客户端的请求,首先由查询优化器进行基于知识网格的优化,产生执行计划后再交给执行引擎去处理。
下图为执行流程:
【StoneDB join 算法分析】查询模块_第10张图片

  • 基于知识网格中的信息进行粗燥集(Rough Set)构建, 并确定此次请求所需使用到的数据节点。

    • 基于 KN 和 MD ,确定查询涉及到的 DN 节点集合,并将 DN 节点归类:

      • 关联 DN 节点:满足查询条件限制的数据节点(直接读取并返回)
      • 不确定性 DN 节点: 数据节点中部分数据满足查询条件(解压后进行处理再返回).
      • 非关联 DN 节点: 与查询条件完全不相关(直接忽略).
    • 执行计划构建时, 会完全规避非关联 DN, 仅读取并解压关联 DN, 按照特定情况决定是否读取不确定的 DN。

      • 例子: 对于一个查询请求, 通过KG可以确定 3 个关联性 DN 和 1 个不确定性 DN。如果此请求包含聚合函数。此时只需要解压不确定性 DN 并计算聚合值,再结合3个关联性 DN 中 MD上的统计值即可得出结果。如果此请求需要返回具体数据, 那么无论关联性 DN 还是不确定性 DN,都需要读取数据块并解压缩,以获得结果集。
  • 如果查询请求的结果可以直接从元信息节点 (MD) 中产生(例如 count, max, min 等操作),则直接返回元信息节点中的数据,无需访问物理数据文件.
  • 查询案例1:SELECT count(*) FROM students where score < 550。

    • 通过 KG 知识,查找包含 score < 550 的数据节点(DN),此处可以看到只有 A/B/C 三个节点涉及到该查询。
    • 数据节点 A 与 B 属于关联 DN 节点, 只需直接从对应的 MD 节点中获取 count 值即可。
    • 数据节点 C 属于不确定性 DN 节点,需要读取数据块并解压, 执行函数计算后才能返回结果集。
    • 这里只有数据块C会被读取并解压,数据节点 A 与 B 并不消耗 IO 资源。

【StoneDB join 算法分析】查询模块_第11张图片

7. 优化算法

7.1 Comment Lookup

Comment Lookup 只能显式地使用在 char 或者 varchar 上面。Comment Lookup可以减少存储空间,提高压缩率,对 char 和 varchar 字段采用 comment lookup 可以提高查询效率。
Comment Lookup 实现机制很像位图索引,实现上利用简短的数值类型替代char字段已取得更好的查询性能和压缩比率。Comment Lookup 的使用除了对数据类型有要求,对数据也有一定的要求。一般要求数据类别的总数小于10000并且当前列的单元数量/类别数量大于10。Comment Lookup 比较适合年龄,性别,省份这一类型的字段。
Comment Lookup 使用很简单,在创建数据库表的时候如下定义即可:

act char(15) comment 'lookup',
part char(4) comment 'lookup',

7.2 join算法

StoneDB 支持 NESTED LOOP JOIN,SORT MERGE JOIN,HASH JOIN和MAP JOIN 算法。
【StoneDB join 算法分析】查询模块_第12张图片
StoneDB 通过 EvaluateConditionJoinWeight 评估 join 权重,根据 UpdateJoinCondition 改变 join 条件,ChooseJoinAlgorithm 选择join算法。

7.2.1 NESTED LOOP JOIN

对于被连接的数据子集较小的情况,嵌套循环连接是个较好的选择。在嵌套循环中,内表被外表驱动,外表返回的每一行都要在内表中检索找到与它匹配的行,因此整个查询返回的结果集不能太大(大于1 万不适合),要把返回子集较小表的作为外表,CBO 默认外表是驱动表 (CBO:基于成本优化),而且在内表的连接字段上一定要有索引。通过 JoinerGeneral::ExecuteOuterJoinLoop 函数进入 NESTED LOOP JOIN 计算。

7.2.2 SORT MERGE JOIN

通常情况下散列连接的效果都比排序合并连接要好,然而如果行源已经被排过序,在执行排序合并连接时不需要再排序了,这时排序合并连接的性能会优于散列连接。

Sort Merge join 用在没有索引,并且数据已经排序的情况。函数 JoinerSort::ExecuteJoinConditions 进入算法,Join关联过程遵循小表扫描 MarkUsedDims(dims1),大表匹配 MarkUsedDims(dims2)。

7.2 HASH JOIN

散列连接是 CBO 做大数据集连接时的常用方式,优化器使用两个表中较小的表(或数据源)利用连接键在内存中建立散列表,然后扫描较大的表并探测散列表,找出与散列表匹配的行。
这种方式适用于较小的表完全可以放于内存中的情况,这样总成本就是访问两个表的成本之和。但是在表很大的情况下并不能完全放入内存,这时优化器会将它分割成若干不同的分区,不能放入内存的部分就把该分区写入磁盘的临时段,此时要有较大的临时段从而尽量提高 I/O 的性能。通过函数 JoinerHash::ExecuteJoinConditions 进入 JoinerHash::ExecuteJoin 执行 HASH JOIN 算法,如果 ini_join_parallel>0,调用ProxyHashJoiner/ParallelHashJoiner 并行执行。

7.2.4 MAP JOIN

Map join 适用于一个大表和一个或多个小表执行 join 操作的场景。整个join过程包含 map、shuffle 和reduce 三个阶段。通常情况下,join 操作在 reduce 阶段执行表连接。map join 操作是在 map 阶段执行的,大量缩短了数据传输的时间,提升了系统资源的利用率,从而起到了优化作业的作用,并且 map join 会将指定的小表全部加载到执行 join 操作的程序的内存中,从而加快 join 的速度。

通过 JoinerMapped::ExecuteJoinConditions 进入MAP JOIN算法,根据 JoinerMapped::GenerateFunction 生产map函数,调用 map_function 的 Init 执行 map join。

你可能感兴趣的:(数据库mysql)