概述

在查询语句中有谓词的情况下,能够有效减少在执行过程中需要处理的数据量。为此,OceanBase 中设置了谓词移动规则,其允许系统利用查询语句各组成部分中已存在的谓词,推导生成新的谓词,并将这些新生成的或原有的谓词尽可能地向数据读取的初始阶段下推,减少了在各个处理阶段所需处理的数据量,从而优化了整体查询效率。

谓词移动的基本原理

SELECT * FROM 
	(SELECT * FROM t1 WHERE c2 < 1000) v1, 
	(SELECT * FROM t2 WHERE c2 > 0) v2
WHERE v1.c1 = v2.c1 AND v1.c2 > v2.c2 AND v1.c1 > 0;

对于上述查询,可以对其进行谓词推导和下推改写,从而减少需要读取的数据量,如下所示:

SELECT * FROM 
	(SELECT * FROM t1 WHERE c1 > 0 AND c2 > 0 AND c2 < 1000) v1, 
	(SELECT * FROM t2 WHERE c1 > 0 AND c2 > 0 AND c2 < 1000) v2
WHERE v1.c1 = v2.c1 AND v1.c2 > v2.c2; 

代码解析

谓词移动规则的入口为ObTransformPredicateMoveAround::transform_one_stmt,执行流程如下:

  1. 调用pullup_predicates函数对满足条件的视图表或集合子查询中的谓词执行提升收集。
  2. 调用pushdown_predicates函数对父查询中的谓词结合前一步收集的谓词执行下推。

pullup_predicates函数负责递归地将视图表中的谓词收集到集合中,执行流程如下:

  1. 调用acquire_transform_params函数为当前语句分配谓词集合(下称input_pullup_preds),供后续流程使用。
  2. 如果当前语句为集合语句,则执行如下流程进行处理:
    1. 调用pullup_predicates_from_set函数对集合语句子查询中的谓词进行提升收集。
    2. 调用generate_set_pullup_predicates函数提取pullup_preds中能够向上提升的谓词(即涉及的参数列均位于集合语句的select列表中的谓词),用于返回父查询。
    3. 调用acquire_transform_params函数获取为当前语句分配的谓词集合,将收集到的谓词保存到该集合中。
  3. 如果当前语句不为集合语句,则执行如下流程进行处理:
    1. 调用preprocess函数将semi信息及join表中能够提升的条件移动到当前语句的where条件中。
    2. 调用pullup_predicates_from_view函数将当前语句的视图表中的谓词收集到input_pullup_preds中。
    3. 如果当前语句为视图查询语句或集合子查询,则调用generate_pullup_predicates函数结合当前语句的where条件、having条件及input_pullup_preds中的谓词进行推导,将得到的谓词集合返回父查询。

pullup_predicates_from_set函数会遍历集合语句的子查询,对其中的谓词进行收集,执行流程如下:

  1. 调用pullup_predicates函数收集子查询语句中的谓词。
  2. 调用rename_set_op_predicates函数将上述谓词涉及的子查询中的列表达式转换为集合语句的列表达式。
  3. 调用check_pullup_predicates函数将各子查询得到的谓词集合进行合并,根据集合操作的类型,执行逻辑如下:
    1. 如果集合操作为union,则合并时取谓词集合的交集。
    2. 如果集合操作为intersect,则合并时取谓词集合中不在交集中的谓词。
    3. 如果集合操作为except,则合并时直接使用第一条子查询的谓词集合。

pullup_predicates_from_view函数负责提升收集当前查询语句下的视图表中的谓词,执行流程如下:

  1. 调用get_columns_in_filters函数抽取当前语句的where条件、having条件、join表连接条件、半连接条件及参数指定的select列表达式涉及的列(下称filter_columns)。
  2. 遍历当前语句的表信息,对于其中的视图表,如果其没有作为外连接的内表,则执行如下流程对其中的谓词进行提升收集:
    1. 调用choose_pullup_columns函数从filter_columns中筛选属于当前视图表的列(下称view_sel_list)。
    2. 调用pullup_predicates函数对视图查询中view_sel_list中的列的谓词进行提升收集。
    3. 调用rename_pullup_predicates函数将收集的谓词集合中视图查询的select列转换为对应父查询中的列。

generate_pullup_predicates函数负责利用已有谓词进行推导,该函数会使用从视图表收集的谓词、当前语句的where条件和having条件的并集作为输入谓词,调用compute_pullup_predicates函数执行推导,处理流程如下:

  1. 使用父查询指定的select列偏移,收集视图查询中对应的select列作为目标列。
  2. 调用transform_predicates函数,使用上述输入谓词和目标列进行谓词推导。
  3. 遍历推导结果中的谓词,将满足如下条件的结果添加到最终的谓词集合中:
    1. 当前谓词应该为简单谓词(>, >=, <, <=, =)或通用谓词(between, like, <>, in),且不能为常量表达式。
    2. 当前谓词涉及的参数应该都包含于目标列中。

transform_predicates函数负责使用输入谓词和目标列进行谓词推导,对原有谓词集合进行扩展,执行流程如下:

    1. 将输入谓词划分为可以参与推导图构建的谓词集合(即valid_preds)和其他谓词集合(即other_preds)。
    2. 使用valid_preds构建推导图,然后按照如下流程进行谓词推导:
      1. 调用ObPredicateDeduce::deduce_simple_predicates函数,基于谓词参数的比较关系传导性,推导出普通谓词(下称simple_preds)。
      2. 调用ObPredicateDeduce::deduce_general_predicates函数,基于谓词参数的比较关系传导性,推导出通用谓词((下称general_preds)。
      3. 调用ObPredicateDeduce::deduce_aggr_bound_predicates函数,基于谓词参数的比较关系传导性,推导出聚合范围谓词(下称aggr_bound_preds)。
    3. 将simple_preds、other_preds、general_preds和aggr_bound_preds中的谓词合并到集合中。

pushdown_predicates负责将提升收集得到谓词进行下推,执行流程如下:

  1. 调用acquire_transform_params函数获取提升收集阶段得到的谓词集合(下称pullup_preds)。
  2. 如果当前语句为集合语句,则调用pushdown_into_set_stmt函数将谓词下推到集合语句的子查询中。
  3. 如果当前语句不为集合语句,则执行如下流程进行处理:
    1. 调用pushdown_through_winfunc函数获取参数谓词集合中能够通过窗口函数继续下推的谓词(下称candi_preds)。
    2. 调用pushdown_into_having函数将candi_preds中的谓词推导下推至having条件中。
    3. 调用pushdown_through_groupby函数基于having条件构建下推谓词集合作为新的candi_preds。
    4. 调用pushdown_into_where函数将candi_preds中的谓词推导下推至where条件中。
    5. 遍历查询语句中的semi信息,调用pushdown_into_semi_info函数将where条件中的谓词推导下推至半连接条件及右表中。
    6. 遍历查询语句中的from表,调用pushdown_into_table函数将where条件中谓词推导下推至from表中。

pushdown_into_set_stmt函数会遍历集合语句的子查询,将谓词下推到子查询语句中,执行流程如下:

  1. 对子查询调用重载的pushdown_into_set_stmt函数进行下推,后者执行流程如下:
    1. 调用rename_set_op_predicates函数将下推谓词涉及的集合语句的列表达式转换为子查询中的列表达式。
    2. 调用pushdown_predicates函数将谓词下推到子查询语句中。
    3. 将参数谓词集合更新为没有成功下推的谓词。
  2. 调用check_pushdown_predicates函数将各子查询没有下推的谓词集合进行合并,根据集合操作的类型,执行逻辑如下:
    1. 如果集合操作为union,则合并时取谓词集合的并集。
    2. 如果集合操作为intersect,则合并时取谓词集合的交集。
    3. 如果集合操作为except,则合并时直接使用第一条子查询的谓词集合。

pushdown_through_winfunc函数首先会遍历当前语句中的窗口函数,找到共同的分区列(下称common_part_exprs)。然后遍历所有的下推谓词,将不包含窗口函数且涉及的列为common_part_exprs子集的表达式加入新的下推集合中。

pushdown_into_having函数首先会抽取pullup_preds中from表的谓词(下称input_preds)以及查询语句的列集合中属于from表的列(下称columns),使用input_preds、candi_preds和查询语句的having条件的并集作为输入谓词,columns作为目标列进行谓词推导,然后将过滤掉pullup_preds中的谓词后的推导结果设置为新的having条件。

pushdown_through_groupby函数会遍历having条件中的条件表达式,然后将满足下推条件的谓词经过转换后加入下推集合并从having条件中移除,处理逻辑如下:

  1. 如果当前条件为关系比较表达式,而其中的左右参数分别为min/max聚合函数和常量表达式,且当前语句只包含一个聚合项时,则可以用聚合函数的参数列替代聚合函数构成新的谓词加入谓词集合中。
  2. 如果当前条件涉及的列为group by表达式的子集,则将该条件加入谓词集合。

pushdown_into_where函数首先会抽取pullup_preds中from表的谓词(下称input_preds)以及查询语句的列集合中属于from表的列(下称columns),使用input_preds、candi_preds和查询语句的where条件的并集作为输入谓词,columns作为目标列进行谓词推导,然后将过滤掉pullup_preds中的谓词后的推导结果设置为新的where条件。

pushdown_into_semi_info函数首先会抽取pullup_preds中属于当前semi join左(右)表的谓词、where条件中的属于当前seim join左表的谓词,以及查询语句的列集合中属于当前seim join右表的列(下称cols),将上述谓词集合的并集(下称properites)与当前semi信息中的连接条件一同作为输入谓词,cols作为目标列进行谓词推导,然后将过滤掉properites中的谓词后的推导结果设置为新的连接条件。对于连接条件中的右表谓词,该函数会继续调用pushdown_into_table函数,尝试将其推入右表中。如果此时连接条件中还有没有被成功下推的右表谓词,则调用ObTransformUtils::pushdown_semi_info_right_filter函数将其推入右表视图的where条件中(如果右表不是视图表,则为其创建视图)。

pushdown_into_table函数负责将谓词下推至表的内部,执行流程如下:

  1. 调用get_pushdown_predicates函数,获取属于当前表的下推谓词集合。
  2. 根据表的类型,执行如下下推逻辑:
    1. 如果表为基表,则不进行下推。
    2. 如果表为视图表,则按照如下流程进行下推:
      1. 调用rename_pushdown_predicates函数将谓词中视图表导出的列替换为视图查询的select列。
      2. 调用choose_pushdown_preds函数选择下推谓词中符合下推条件的谓词,即不包含子查询的谓词。
      3. 调用pushdown_predicates函数将符合条件的谓词下推至视图查询中。
    3. 如果表为join表,则调用pushdown_into_joined_table函数进行下推。
  3. 从谓词参数集合中移除成功下推的谓词。

pushdown_into_joined_table函数负责将谓词下推至join表,执行流程如下:

  1. 如果当前连接不为full连接,则将谓词下推至当前连接条件,执行流程如下:
    1. 抽取查询语句的列集合中属于当前连接内表侧的列(即左连接的右表、右连接的左表及内连接的左右两表中的列,下称cols),将pullup_preds和下推谓词的并集(下称properites)与当前连接条件一同作为输入谓词(下称all_preds),cols作为目标列进行谓词推导,得到新的谓词集合(下称new_preds)。
    2. 根据连接类型,执行如下下推逻辑:
      1. 如果为内连接,则使用new_preds中不在properites中谓词作为新的连接条件。
      2. 如果为外连接,则使用原连接条件中的谓词与new_preds中属于内表侧条件且不在all_preds中谓词的并集作为新的连接条件。
  2. 如果当前连接不为full连接,则将谓词下推至子节点,执行流程如下:
    1. 为左右节点选择下推谓词。如果当前连接为左连接,则将参数谓词集合作为左下推参数(下称left_down),连接条件作为右下推参数(下称right_down),右连接则相反;如果为当前连接为内连接,则将参数谓词集合和连接条件的并集作为左右下推参数。
    2. 使用上一步得到的下推参数,对左右节点分别调用pushdown_into_table函数。
    3. 将参数谓词集合和连接条件更新为没有被成功下推的谓词。如果当前连接为左连接,则将参数谓词集合设置为left_down,连接条件设置为right_down,右连接则相反;如果为当前连接为内连接,则将参数谓词集合中同时存在于left_down和right_down中的谓词集合作为新的参数谓词集合,连接条件中同时存在于left_down和right_down中的谓词集合作为新的连接条件。
Logo

了解最新的技术洞察和前沿趋势,参与 OceanBase 定期举办的线下活动,与行业开发者互动交流

更多推荐