目录
背景
优化规则HiveReduceExpressionsWithStatsRule
总结
背景
这篇文章来讲优化规则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的表结构如下:

这些统计信息里面含有以下信息:
此优化规则就是根据收集的统计信息,判断Filter的谓词表达式,是否可直接简化掉,而不是再执行时做不必要的谓词判断。
优化规则HiveReduceExpressionsWithStatsRule
1)matches方法逻辑详解
matches方法返回此规则Rule是否可能与给定的操作数operands匹配,但是此方法的任何实现都可以给出误报,也就是说虽然规则与操作数匹配,但随后具OnMatch(ReloptRuleCall)而不生成任何后续任务。
判断由RelOptCall调用的优化规则Rule是否与输入参数RelNode关系表达式匹配,即此优化规则Rule能否应用到一个RelNode关系表达式树上。但此matches方法是继承自父类方法,默认返回true。
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类
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中,以备优化器估算成本构建最优执行计划使用。
@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<最大值,最小值>。
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来进行谓词表达式简化操作。谓词表达式比较情况分以下几种:
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等等简化操作优化。