目录
背景
优化规则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
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等等简化操作优化。