Hive优化器原理与源码解析系列--优化规则HiveReduceExpressionsWithStatsRule(二十三)

2022-04-25 15:43:14 浏览数 (1)

目录

背景

优化规则HiveReduceExpressionsWithStatsRule

  • matches方法逻辑详解
  • onMatch方法逻辑详解

总结

背景

这篇文章来讲优化规则HiveReduceExpressionsWithStatsRule,主要功能是使用列统计Stats信息,来简化Filter过滤器条件。例如:通过统计信息知道a最大值为4,则a>5永远为false。当前仅支持的=, >=, <=, >, < 和 In操作判断简化。

在HiveMeta元数据信息中,统计信息收集在表TAB_COL_STATS或PART_COL_STATS收集了每列的为NUM_DISTINCTS的记录数,TAB_COL_STATS是非分区表的统计信息,而PART_COL_STATS是表分区级别的统计信息,两者收集的统计信息维度相同。这里举例PART_COL_STATS的表结构如下:

这些统计信息里面含有以下信息:

  • DB_NAME 数据库名称
  • TABLE_NAME表名称
  • PARTITION_NAME分区名称
  • AVG_COL_LEN 列平均长度,
  • COLUMN_NAME 列名称,
  • COLUMN_TYPE 数据类型
  • LAST_ANALYZED最新统计日期
  • MAX_COL_LEN 此列最大长度,
  • NUM_DISTINCTS 此列唯一值个数,又缩写为NDV
  • NUM_FALSES 此列为False个数
  • NUM_NULLS 此列为NULLS个数
  • NUM_TRUES 此列为Trues个数

此优化规则就是根据收集的统计信息,判断Filter的谓词表达式,是否可直接简化掉,而不是再执行时做不必要的谓词判断。

优化规则HiveReduceExpressionsWithStatsRule

1)matches方法逻辑详解

matches方法返回此规则Rule是否可能与给定的操作数operands匹配,但是此方法的任何实现都可以给出误报,也就是说虽然规则与操作数匹配,但随后具OnMatch(ReloptRuleCall)而不生成任何后续任务。

判断由RelOptCall调用的优化规则Rule是否与输入参数RelNode关系表达式匹配,即此优化规则Rule能否应用到一个RelNode关系表达式树上。但此matches方法是继承自父类方法,默认返回true。

代码语言:javascript复制
public boolean matches(RelOptRuleCall call)
{  
  return true;
}

2)onMatch方法逻辑详解

接收有关一条规则匹配的通知。同时此方法被调用,call.rels保存了与规则Rule的操作数Operands匹配上的关系表达式RelNode集合;call.rels[0]是根表达式。通常一条规则Rule会检查这些节点是否有效匹配,创建一个新表达式RelNode(等价的)然后调用RelOptRuleCall.transformTo(org.apache.calcite.rel.RelNode, java.util.Map<org.apache.calcite.rel.RelNode, org.apache.calcite.rel.RelNode>)注册表达式。而RelOptRuleCall用一系列RelNode关系表达式集合作为参数,对RelOptRule优化规则的调用。

从表达式操作符树取call.rel(0)根Root RelNode表达式 Filter操作对象。RexUtil.pullFactors创建的等价版本一个节点,在该版本中,将上拉ORs之间的公共因子。即通过从DNF表达式中提取公共元素来重新组合过滤器。

何为合取范式(CNF)和析取范式(DNF),这里简单介绍一下。析取范式(DNF)为OR连接的谓词表达式,合取范式(CNF)为AND连接谓词表达式,并且OR连接谓词表达式和AND连接的表达式可相互转换(详解参考优化规则HivePreFilteringRule(十五)文末有相关连接)。

使用RexReplacer类继承RexShuttle类

代码语言:javascript复制
public class RexShuttle
extends java.lang.Object
implements RexVisitor<RexNode>

Passes over a row-expression, calling a handler method for each node, appropriate to the type of the node.Like RexVisitor, this is an instance of the Visitor Pattern. Use RexShuttle if you would like your methods to return a value.

对一个操作符树的遍历有两种模式:一访问器模式,二监听者模式。使用的访问器模式,会有返回值。

通过对RelNode关系表达式树的遍历,来缩减替换表达式,生成的Filter谓词表达式newFilterCondition。如果经过简化后谓词表达式不想等,即相比原来的,已经做了简化。使用新生成newFilter注册到RelSet中,以备优化器估算成本构建最优执行计划使用。

代码语言:javascript复制
@Override
public void onMatch(RelOptRuleCall call) {
  final Filter filter = call.rel(0);
  final RexBuilder rexBuilder = filter.getCluster().getRexBuilder();
  final RelMetadataQuery metadataProvider = RelMetadataQuery.instance();
  //1.可能通过从DNF表达式中提取公共元素来重新组合过滤器
  RexNode newFilterCondition = RexUtil.pullFactors(rexBuilder, filter.getCondition());
  //2.用统计信息,简化Filter过滤器
  RexReplacer replacer = new RexReplacer(filter, rexBuilder, metadataProvider);//遍历访问器
  newFilterCondition = replacer.apply(newFilterCondition);//遍历进行简化,生成简化后谓词表达式
  //3.如果创建了新的Filter过滤器则变换
  if (!filter.getCondition().toString().equals(newFilterCondition.toString())) {
    Filter newFilter = filter.copy(filter.getTraitSet(), filter.getInput(), newFilterCondition);
    call.transformTo(newFilter);//转换优化
  }
}

根据HiveMeta元数据信息,提取该字段RexInputRef对象最大值Max和最小值Min范围键值对。RelColumnOrigin是用于描述由RelNode关系表达式生成的输出列来源的一种数据结构。通过RelColumnOrigin对象columnOrigin获取RelOptHiveTable表对象,根据表对象table获取统计信息,并判断该统计信息是否最新的,然后取该字段RexInputRef的最大值和最小值,返回Pair.<Number,Number>of(max, min) 即Pair<最大值,最小值>。

代码语言:javascript复制
 private Pair<Number,Number> extractMaxMin(RexInputRef ref) {
      Number max = null;
      Number min = null;
      RelColumnOrigin columnOrigin = this.metadataProvider.getColumnOrigin(filterOp, ref.getIndex());
      if (columnOrigin != null) {
        RelOptHiveTable table = (RelOptHiveTable) columnOrigin.getOriginTable();//获取Hive table表对象
        if (table != null) {
          //ColStatistics对应的统计信息表字段信息
          ColStatistics colStats =
                 table.getColStat(Lists.newArrayList(columnOrigin.getOriginColumnOrdinal())).get(0);
          if (colStats != null && StatsSetupConst.areColumnStatsUptoDate(//判读该统计信息是否为最新
                  table.getHiveTableMD().getParameters(), colStats.getColumnName())) {
            Range range = colStats.getRange();//该列值范围
            if (range != null) {
              max = range.maxValue;//最大值
              min = range.minValue;//最小值
            }
          }
        }
      }
      return Pair.<Number,Number>of(max, min);
    }

以上就是获取该列的Pair<最大值,最小值>,用来判断谓词表达式是否可简化的依据。

根据HiveMeta元数据的统计信息中,获取此列Column的最大值和最小值。通过判断谓词表达式中比较操作符与常量Constant的比较(RexLiteral 常量对象),判断这个谓词表达式结果是True或False来进行谓词表达式简化操作。谓词表达式比较情况分以下几种:

  • 谓词表达式的比较符号“=”,此常量值小于最小值或大于最大值,则返回false常量的RexNode行表达式
  • 谓词表达式的比较符号“>”,此常量值小于最小值,返回true;此常量值大于或等于最大值,则返回false
  • 谓词表达式的比较符号“>=”,此常量值小于或等于最小值,返回true;此常量值大于最大值,则返回false
  • 谓词表达式的比较符号“<”,此常量值小于或等于最小值,返回false;此常量值大于最大值,则返回true
  • 谓词表达式的比较符号“<=”,此常量值小于最小值,返回false;此常量值大于或等于最大值,则返回true
代码语言:javascript复制
private RexNode reduceCall(RexLiteral literal, SqlKind kind, Number max, Number min) {
{
  // Stats were available, try to reduce
  if (max != null && min != null) {
    BigDecimal maxVal = new BigDecimal(max.floatValue());
    BigDecimal minVal = new BigDecimal(min.floatValue());
    RexLiteral maxLiteral = rexBuilder.makeExactLiteral(maxVal, literal.getType());//转换为常量表达式
    RexLiteral minLiteral = rexBuilder.makeExactLiteral(minVal, literal.getType());
    //负整数、零或正整数,因为此对象小于、等于或大于指定对象。
    if (kind == SqlKind.EQUALS) {//1、谓词表达式的操作符号“=”
      if (minLiteral.getValue().compareTo(literal.getValue()) > 0 ||//小于最小值
              maxLiteral.getValue().compareTo(literal.getValue()) < 0) { //大于最大值
        return rexBuilder.makeLiteral(false);//返回False常量
      }
    }
    if (kind == SqlKind.GREATER_THAN) {//2、谓词表达式的操作符号“>”
      if (minLiteral.getValue().compareTo(literal.getValue()) > 0) {
        return rexBuilder.makeLiteral(true);
      } else if (maxLiteral.getValue().compareTo(literal.getValue()) <= 0) {
        return rexBuilder.makeLiteral(false);
      }
    } else if (kind == SqlKind.GREATER_THAN_OR_EQUAL) {//3、谓词表达式的操作符号“>=”
      if (minLiteral.getValue().compareTo(literal.getValue()) >= 0) {
        return rexBuilder.makeLiteral(true);
      } else if (maxLiteral.getValue().compareTo(literal.getValue()) < 0) {
        return rexBuilder.makeLiteral(false);
      }
    } else if (kind == SqlKind.LESS_THAN) {//4、谓词表达式的操作符号“<”
      if (minLiteral.getValue().compareTo(literal.getValue()) >= 0) {
        return rexBuilder.makeLiteral(false);
      } else if (maxLiteral.getValue().compareTo(literal.getValue()) < 0) {
        return rexBuilder.makeLiteral(true);
      }
    } else if (kind == SqlKind.LESS_THAN_OR_EQUAL) {//5、谓词表达式的操作符号“<=”
      if (minLiteral.getValue().compareTo(literal.getValue()) > 0) {
        return rexBuilder.makeLiteral(false);
      } else if (maxLiteral.getValue().compareTo(literal.getValue()) <= 0) {
        return rexBuilder.makeLiteral(true);
      }
    }
  }
  return null;
}

总结

优化规则HiveReduceExpressionsWithStatsRule使用HiveMeta元数据的统计信息,来对Filter谓词表达式做简化操作,而FilterReduceExpressionsRule优化规则,是对列自身谓词逻辑判断,如冗余cast转换移除,cast转换为字段本身的相同的数据类型;Filter内含有条件是常量,恒为True等等简化操作优化。

0 人点赞