Hive谓词解析过程分析

where col1 = 100 and abs(col2) > 0在Hive中的处理过程

where过滤条件称为谓词predicate。

以上where过滤条件在经过Hive的语法解析后,生成如下的语法树:

TOK_WHERE                   AND                      =                     TOK_TABLE_OR_COL   c1              100                >                     TOK_FUNCTION       ABS             TOK_TABLE_OR_COLc2           0                  

有了语法树之后,最终的目的是生成predicate每个节点对应的ExprNodeDesc,即描述对应的节点:

  public Map<ASTNode, ExprNodeDesc> genAllExprNodeDesc(ASTNode expr, RowResolver input,TypeCheckCtx tcCtx) throws SemanticException {...Map<ASTNode, ExprNodeDesc> nodeOutputs =TypeCheckProcFactory.genExprNode(expr, tcCtx);...

生成的过程是对上述语法树的一个深度优先遍历的过程,Hive中大量对树的遍历的代码,在遍历过程中根据指定的规则或对语法树进行修改,或输出相应的结果。

Hive中有一个默认的深度优先遍历的实现DefaultGraphWalker。

这个遍历器的实现部分代码如下:

  public void walk(Node nd) throws SemanticException {    // Push the node in the stackopStack.push(nd);// While there are still nodes to dispatch...while (!opStack.empty()) {Node node = opStack.peek();if (node.getChildren() == null ||getDispatchedList().containsAll(node.getChildren())) {// Dispatch current nodeif (!getDispatchedList().contains(node)) {dispatch(node, opStack);opQueue.add(node);}opStack.pop();continue;}// Add a single child and restart the loopfor (Node childNode : node.getChildren()) {if (!getDispatchedList().contains(childNode)) {opStack.push(childNode);break;}}} // end while}

先将当前节点放到待处理的栈opStack中,然后从opStack取节点出来,如果取出来的节点没有Children,或者Children已经全部处理完毕,才对当前节点进行处理(dispatch),如果当前节点有Children且还没有处理完,则将当前节点的Children放到栈顶,然后重新从栈中取节点进行处理。这是很基础的深度优先遍历的实现。

那在遍历的过程中,如何针对不同的节点进行不同的处理呢?

在遍历之前,先预置一些针对不同的节点不同规则的处理器,然后在遍历过程中,通过分发器Dispatcher选择最合适的处理器进行处理。

生成ExprNodeDesc的遍历中一共先预置了8个规则Rule,每个规则对应一个处理器NodeProcessor:

    Map<Rule, NodeProcessor> opRules = new LinkedHashMap<Rule, NodeProcessor>();opRules.put(new RuleRegExp("R1", HiveParser.TOK_NULL + "%"),tf.getNullExprProcessor());opRules.put(new RuleRegExp("R2", HiveParser.Number + "%|" +HiveParser.TinyintLiteral + "%|" +HiveParser.SmallintLiteral + "%|" +HiveParser.BigintLiteral + "%|" +HiveParser.DecimalLiteral + "%"),tf.getNumExprProcessor());opRules.put(new RuleRegExp("R3", HiveParser.Identifier + "%|"+ HiveParser.StringLiteral + "%|" + HiveParser.TOK_CHARSETLITERAL + "%|"+ HiveParser.TOK_STRINGLITERALSEQUENCE + "%|"+ "%|" + HiveParser.KW_IF + "%|" + HiveParser.KW_CASE + "%|"+ HiveParser.KW_WHEN + "%|" + HiveParser.KW_IN + "%|"+ HiveParser.KW_ARRAY + "%|" + HiveParser.KW_MAP + "%|"+ HiveParser.KW_STRUCT + "%|" + HiveParser.KW_EXISTS + "%|"+ HiveParser.KW_GROUPING + "%|"+ HiveParser.TOK_SUBQUERY_OP_NOTIN + "%"),tf.getStrExprProcessor());opRules.put(new RuleRegExp("R4", HiveParser.KW_TRUE + "%|"+ HiveParser.KW_FALSE + "%"), tf.getBoolExprProcessor());opRules.put(new RuleRegExp("R5", HiveParser.TOK_DATELITERAL + "%|"+ HiveParser.TOK_TIMESTAMPLITERAL + "%"), tf.getDateTimeExprProcessor());opRules.put(new RuleRegExp("R6",HiveParser.TOK_INTERVAL_YEAR_MONTH_LITERAL + "%|"+ HiveParser.TOK_INTERVAL_DAY_TIME_LITERAL + "%|"+ HiveParser.TOK_INTERVAL_YEAR_LITERAL + "%|"+ HiveParser.TOK_INTERVAL_MONTH_LITERAL + "%|"+ HiveParser.TOK_INTERVAL_DAY_LITERAL + "%|"+ HiveParser.TOK_INTERVAL_HOUR_LITERAL + "%|"+ HiveParser.TOK_INTERVAL_MINUTE_LITERAL + "%|"+ HiveParser.TOK_INTERVAL_SECOND_LITERAL + "%"), tf.getIntervalExprProcessor());opRules.put(new RuleRegExp("R7", HiveParser.TOK_TABLE_OR_COL + "%"),tf.getColumnExprProcessor());opRules.put(new RuleRegExp("R8", HiveParser.TOK_SUBQUERY_OP + "%"),tf.getSubQueryExprProcessor());

这里使用的分发器Dispatcher是DefaultRuleDispatcher,DefaultRuleDispatcher选择处理器的逻辑如下:

    // find the firing rule// find the rule from the stack specifiedRule rule = null;int minCost = Integer.MAX_VALUE;for (Rule r : procRules.keySet()) {int cost = r.cost(ndStack);if ((cost >= 0) && (cost <= minCost)) {minCost = cost;rule = r;}}NodeProcessor proc;if (rule == null) {proc = defaultProc;} else {proc = procRules.get(rule);}// Do nothing in case proc is nullif (proc != null) {// Call the process functionreturn proc.process(nd, ndStack, procCtx, nodeOutputs);} else {return null;}

遍历所有的规则Rule,调用每个规则的cost方法计算cost,找其中cost最小的规则对应的处理器,如果没有找到,则使用默认处理器,如果没有设置默认处理器,则不做任何事情。

那么每个规则的cost是如何计算的?

-- 没太看懂==|| (后续再理理)

WHERE条件语法树每个节点对应的处理器如下:

TOK_WHERE                   AND                        --> TypeCheckProcFactory.DefaultExprProcessor=                       --> TypeCheckProcFactory.DefaultExprProcessorTOK_TABLE_OR_COL     --> TypeCheckProcFactory.ColumnExprProcessorc1                --> TypeCheckProcFactory.StrExprProcessor100                  --> TypeCheckProcFactory.NumExprProcessor>                       --> TypeCheckProcFactory.DefaultExprProcessorTOK_FUNCTION         --> TypeCheckProcFactory.DefaultExprProcessorABS               --> TypeCheckProcFactory.StrExprProcessorTOK_TABLE_OR_COL  --> TypeCheckProcFactory.ColumnExprProcessorc2             --> TypeCheckProcFactory.StrExprProcessor0                    --> TypeCheckProcFactory.NumExprProcessorTypeCheckProcFactory.StrExprProcessor 生成ExprNodeConstantDesc
TypeCheckProcFactory.ColumnExprProcessor 处理column,生成ExprNodeColumnDesc
TypeCheckProcFactory.NumExprProcessor生成ExprNodeConstantDesc
TypeCheckProcFactory.DefaultExprProcessor生成ExprNodeGenericFuncDesc

在深度优先遍历完WHERE语法树后,每个节点都会生成一个ExprNodeDesc,但是其实除了最顶层的AND节点生成的ExprNodeDesc有用,其他的节点生成的都是中间结果,最终都会包含在AND节点生成的ExprNodeDesc中。所以在遍历WHERE树后,通过AND节点生成的ExprNodeDesc构造FilterDesc:

new FilterDesc(genExprNodeDesc(condn, inputRR, useCaching), false)

有了FilterDesc后,就能够构造出FilterOperator了,然后再将生成的FilterOperator加入到Operator树中:

Operator<T> ret = get((Class<T>) conf.getClass());
ret.setConf(conf);

至此,where过滤条件对应的FilterOperator构造完毕。

接下来仔细看下AND生成的ExprNodeDesc,它其实是一个ExprNodeGenericFuncDesc:

  // genericUDF是GenericUDFOPAnd,就是对应AND操作符private GenericUDF genericUDF;// AND是一个二元操作符,children里存的是对应的操作符// 根据WHERE语法树,可以知道children[0]肯定又是一个ExprNodeGenericFuncDesc,而且是一个=函   // 数,而children[1]也是一个肯定又是一个ExprNodeGenericFuncDesc,而且是一个>函数,以此类     // 推,每个ExprNodeGenericFuncDesc都有对应的childrenprivate List<ExprNodeDesc> chidren;// UDF的名字,这里是andprivate transient String funcText;/*** This class uses a writableObjectInspector rather than a TypeInfo to store* the canonical type information for this NodeDesc.*/private transient ObjectInspector writableObjectInspector;

每个ExprNodeDesc都对应有一个ExprNodeEvaluator,来对每个ExprNodeDesc进行实际的计算。看下ExprNodeEvaluator类的基本方法:

public abstract class ExprNodeEvaluator<T extends ExprNodeDesc> {// 对应的ExprNodeDescprotected final T expr;// 在经过这个Evaluator计算后,它的输出值该如何解析的ObjectInspectorprotected ObjectInspector outputOI;.../*** Initialize should be called once and only once. Return the ObjectInspector* for the return value, given the rowInspector.* 初始化方法,传入一个ObjectInspector,即传入的数据应该如何解析的ObjectInspector* 而需要返回经过这个Evaluator计算后的输出值的解析ObjectInspector*/public abstract ObjectInspector initialize(ObjectInspector rowInspector) throws HiveException;// evaluate方法,调用来对row数据进行解析public Object evaluate(Object row) throws HiveException {return evaluate(row, -1);}/*** Evaluate the expression given the row. This method should use the* rowInspector passed in from initialize to inspect the row object. The* return value will be inspected by the return value of initialize.* If this evaluator is referenced by others, store it for them*/protected Object evaluate(Object row, int version) throws HiveException {if (version < 0 || version != this.version) {this.version = version;return evaluation = _evaluate(row, version);}return evaluation;}// 由各个子类实现的方法的_evaluate方法,结合上面的evaluate方法,这里实际使用了设计模式的模板   // 方法模式protected abstract Object _evaluate(Object row, int version) throws HiveException;...
}

通过ExprNodeEvaluatorFactory获取到每个ExprNodeDesc对应的ExprNodeEvaluator:

  public static ExprNodeEvaluator get(ExprNodeDesc desc) throws HiveException {// Constant nodeif (desc instanceof ExprNodeConstantDesc) {return new ExprNodeConstantEvaluator((ExprNodeConstantDesc) desc);}// Column-reference node, e.g. a column in the input rowif (desc instanceof ExprNodeColumnDesc) {return new ExprNodeColumnEvaluator((ExprNodeColumnDesc) desc);}// Generic Function node, e.g. CASE, an operator or a UDF nodeif (desc instanceof ExprNodeGenericFuncDesc) {return new ExprNodeGenericFuncEvaluator((ExprNodeGenericFuncDesc) desc);}// Field node, e.g. get a.myfield1 from aif (desc instanceof ExprNodeFieldDesc) {return new ExprNodeFieldEvaluator((ExprNodeFieldDesc) desc);}throw new RuntimeException("Cannot find ExprNodeEvaluator for the exprNodeDesc = " + desc);}

看下FilterOperator中如何使用ExprNodeEvaluator对数据进行过滤的。

首先在FilterOperator的initializeOp方法中,获取到ExprNodeEvaluator:

conditionEvaluator = ExprNodeEvaluatorFactory.get(conf.getPredicate());

然后在process方法中,调用initialize方法后,调用eveluate方法获取到整个where过滤的结果:

conditionInspector = (PrimitiveObjectInspector) conditionEvaluator.initialize(rowInspector);
...
Object condition = conditionEvaluator.evaluate(row);
...
Boolean ret = (Boolean) conditionInspector.getPrimitiveJavaObject(condition);// 如果结果是true,则forward到下一个operator继续处理
if (Boolean.TRUE.equals(ret)) {forward(row, rowInspector);
}   

再来看下GenericUDFOPAnd的evaluate方法实现:

@Overridepublic Object evaluate(DeferredObject[] arguments) throws HiveException {boolean bool_a0 = false, bool_a1 = false;Object a0 = arguments[0].get();if (a0 != null) {bool_a0 = boi0.get(a0);if (bool_a0 == false) {result.set(false);return result;}}Object a1 = arguments[1].get();if (a1 != null) {bool_a1 = boi1.get(a1);if (bool_a1 == false) {result.set(false);return result;}}if ((a0 != null && bool_a0 == true) && (a1 != null && bool_a1 == true)) {result.set(true);return result;}return null;}

从以上代码知道,在进行AND的计算时,如果左边条件返回false,则不会进行右边条件的计算,所以AND的顺序其实是影响实际的效率的。类似的还有OR也是一样的,如果左边条件返回true,则不会进行右边条件的计算。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/456187.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pandas读写各种类型数据

read_X()通常是pandas模块下的&#xff0c;to_X()是dataframe的方法 CSV 读取 使用pandas.read_csv()方法&#xff0c;返回的是一个dataframe csv默认是以"&#xff0c;"分割的 csv文件内容 1、read_csv()默认以第一行数据作为标题 2、调用dataframe的head()方法…

python 类装饰器

1 装饰器无参数 class tracer: def __init__(self,func): self.calls 0 self.func func def __call__(self,*args): self.calls 1 print(call %s to %s %(self.calls, self.func.__name__)) self.func(*args) tracer def spam(a, b, c): print(a b c) …

【数据分析】使用pandas和numpy分析美国大选献金项目

1. 数据载入与总览 1.1 数据加载 #绘图工具 import matplotlib.pyplot as plt %matplotlib inline #数据处理工具 import numpy as np import pandas as pd from pandas import Series,DataFrame#数据路径自己指定&#xff0c;本案例数据路径就在当前文件夹下面子文件夹usa_e…

《容器技术系列》一1.4 Docker运行案例分析

本节书摘来华章计算机《容器技术系列》一书中的第1章 &#xff0c;第1.4节&#xff0c;孙宏亮 著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。 1.4 Docker运行案例分析 1.3节着重介绍了Docker架构中各个模块的功能&#xff0c;学完后我们可以对Docker的架构有一…

3月22日AM

看了思维章节精讲视频课&#xff0c;并且总结了部分思维章节内容转载于:https://www.cnblogs.com/bgd140206102/p/6601440.html

阿里巴巴Dubbo实现的源码分析

Dubbo概述Dubbo是阿里巴巴开源出来的一个分布式服务框架&#xff0c;致力于提供高性能和透明化的RPC远程服务调用方案&#xff0c;以及作为SOA服务治理的方案。它的核心功能包括&#xff1a; remoting:远程通讯基础&#xff0c;提供对多种NIO框架抽象封装&#xff0c;包括“同步…

H264 CAVLC 研究

目录 1 CAVLC概念 2 CAVLC原理 3 CAVLC编码流程 4 CAVLC解码流程 展开全部 1 CAVLC概念 2 CAVLC原理 3 CAVLC编码流程 4 CAVLC解码流程 收起 摘要纠错编辑摘要 CAVLC即基于上下文的自适应变长编码。H.264标准中使用CAVLC对4*4模块的亮度和色度残差数据进行编码。 CAVLC-CAVLC…

instanceof 的运用

2019独角兽企业重金招聘Python工程师标准>>> Java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出&#xff0c;这个对象是否是这个特定类或者是它的子类的一个实例。 用法&#xff1a; result object i…

R 脚本读取汇总 Excel 表格数据

主要用到了 xlsx 和 rJava 包&#xff0c;打开 Excel 文件&#xff0c;读取各表格数据&#xff0c;再写入到汇总表。 下图为处理前的原始数据表格&#xff1a; 下图为处理后的数据&#xff1a; 代码实现 安装&加载包的函数实现。installed.packages() 函数获取所有已安装…

【大数据】最新大数据学习路线(完整详细版,含整套教程)

大数据学习路线 java(Java se,javaweb) Linux(shell,高并发架构,lucene,solr) Hadoop(Hadoop,HDFS,Mapreduce,yarn,hive,hbase,sqoop,zookeeper,flume) 机器学习(R,mahout) Storm(Storm,kafka,redis) Spark(scala,spark,spark core,spark sql,spark streaming,spark mllib,spa…

谷歌浏览器开发调试工具中Sources面板 js调试等 完全介绍

这次分享的是Chrome开发工具中最有用的面板Sources。 Sources面板几乎是我最常用到的Chrome功能面板&#xff0c;也是在我看来决解一般问题的主要功能面板。通常只要是开发遇到了js报错或者其他代码问题&#xff0c;在审视一遍自己的代码而一无所获之后&#xff0c;我首先就会打…

【Python】最新Python学习路线(完整详细版,含整套教程)

python目前应用最广的三个岗位&#xff1a;全栈开发、数据分析、运维开发&#xff0c;今天我们就以这三个重点的岗位来做一下自学Python的规划&#xff0c;希望你在学之前就能有明确的学习方向。 最近开始整理python的资料&#xff0c;博主建立了一个qq群&#xff0c;希望给大家…

程序员,软件测试知多少?

送给初级程序员的测试认知文作为开发同学&#xff0c;一些基本的测试岗位相关知识还是很有必要了解一下&#xff0c;免的某些同学在工作中和测试同学斗嘴、打架、群殴等以及被测试鄙视....。 我们常常听说的一些测试专业术语&#xff0c;比如白盒、黑盒、单元测试&#xff0c;相…

vue 入门环境搭建

公司项目要用vue.js来开发&#xff0c;要使用vue来开发前端框架&#xff0c;首先要有环境&#xff0c;所以给大家介绍一下如何搭建vue环境。其实很简单&#xff1a; 1.首先下载安装node.js。 去官网https://nodejs.org/zh-cn/下载安装包。 2.安装webpack 打开cmd命令界面&#…

【解决】Win10修改host没有权限问题

Step1&#xff1a;右键文件选择属性&#xff0c;选择安全&#xff0c;点击编辑&#xff1a; Step2&#xff1a;在弹窗中点击添加&#xff0c;在弹窗中点击高级&#xff1a; Step3&#xff1a;在弹窗中点击立即查找&#xff0c;选中当前用户&#xff0c;点击确定&#xff1a; …

[已授权] 互联网定位技术小谈

​ 诚邀阿里云先知社区邀请&#xff0c;不胜感激&#xff01;今日小编在此为大家介绍一下互联网中所应用的定位技术。互联网的发展日新月异&#xff0c;技术迭代很快&#xff0c;各行各业的智慧在互联网这片蓝天下碰撞结晶&#xff0c;造福大众。今天要讲述的集中定位方式&…

Python的DataFrame切片大全(包含多重索引)

码字不易&#xff0c;喜欢请点赞&#xff01;&#xff01;&#xff01; 摘要 这篇主要讲解如何对pandas的DataFrame进行切片&#xff0c;包括取某行、某列、某几行、某几列、以及多重索引的取数方法。 • 选取行名、列名、值 • 以标签&#xff08;行、列的名字&#xff09;…

什么是数据分析的关键指标?

什么是核心关键指标呢&#xff1f; 这是一个好问题&#xff0c;不过没有标准的答案。企业性质不同&#xff0c;所处行业、发展阶段不同&#xff0c;关注点当然不同。不过大体可以这样来划分。 1、发展阶段不同&#xff0c;需求不同 对于一个想要做数据化管理的企业来说&#xf…

加密函数

MD5&#xff1a;密码为web页面做准备&#xff0c;建议使用MD5 PASSWORD() :修改当前用户或其他用户密码 mysql> SELECT MD5(admin); #对admin进行MD5的加密&#xff08;32位&#xff09;----------------------------------| MD5(admin) |--------------------------------…

如何使用notepad运行python程序

关于使用notepad运行python程序 首先要确保python解释器已经安装成功,查看方法,windows可以在命令提示符中查看,通过按下winR键,调出运行窗口,在输入框中输入cmd回车,然后在命令行中输入python,若出现版本信息,例如Python 3.6.1 (v3.6.1:69c0db5, Mar 21 2017, 18:41:36) [MSC…