调研系列第六篇:HIVE的DML语句执行简介

HIVEDML语句执行简介

1.      执行入口简介

对于一般语句的执行入口都是在Driver.run(String command)这个方法中,runInternalàcompileàexecute

词法+语法解析:调用antlr的解析类,生成一棵ast语法树

语义解析:以嵌套的方式解析出一个sql中各个数据项,调用SemanticAnalyzer.doPhase1,主要是遍历整个ast树结构,找出selettdestgroup等选项。

逻辑执行计划:生成Operatorast执行树结构,一个个的operator(其中有属性表明其父OperatorchildOperator) ,这一步生成的operator其实就已经是可以执行的一个个算子。

Optimizer : 在逻辑执行计划的基础上根据一些规则来重新逻辑执行计划,以达到更高的效率 。

物理执行计划:将优化后的逻辑执行计划,翻译成可在mr上执行的物理执行计划,就是将上面的一个个算子组合起来,然后调用,其输出是一个List<Task>  rootTask 

rootTask的执行:物理执行计划的输入是一堆的List<Task>  rootTask(即最上层的task,没有前置依赖的),task中存储有自己的前置依赖和child节点,每个task有自己的execute方法,按照依赖关系一次调度起来就行了。

2.      词法+语法解析

利用HiveParse(通过hive的语法.g文件生成的)分析sql语句,然后生成一颗有语义的AST树,以以下语句为例

 其语法树的结构如下:

      

一个dml语句最终都翻译成为TOK_QUERY类型的ast树,这棵树可以看成是个平行的结构,主要包含这几部分属性:

TOK_FROM:就是数据来源,这个可以是一个的table或者subqueryjoinunionlateralview(hiveudtf)这几种,其中后面的四种内部又是一个新的嵌套结构,嵌套一个新的 TOK_QUERY,这个TOK_QUERY也包含相同的这些项。

TOK_INSERT:数据的去向,包含一堆平行的结构,TOK_DESTINATION(数据要insert的地方,如果只是简单的selecthive也会翻译出insert到一个tmpfile)TOK_SELECT(对于要插入的数据的select操作)/TOK_SELECTDI(select distinct)TOK_WHERE(filter条件)TOK_GROUPBY(group by)TOK_DISTRIBUTEBY(distributeby)TOK_SORTBY(sort)/TOK_ORDERBY(order)TOK_HAVING(having条件)TOK_LIMIT(limit)这些元素,还有一些不常用的没有列举。

简单的一个语义流程就是:从from的数据源取数据àwhere条件过滤—>group by/distribute by操作--->having条件过滤-àsort by/order by操作  à limit 操作 à写到dest中(这个在hive中叫FileSinkOperator,简称FS)。

只有from后面才会嵌套结构(subqueryjoinunionlateralview),其它的可以看作是一个hql中平行的结构。

  1 import javax.swing.JEditorPane;
  2 import javax.swing.JFrame;
  3 import javax.swing.JPanel;
  4 import javax.swing.JScrollPane;
  5 import javax.swing.JSplitPane;
  6 import javax.swing.UIManager;
  7 import javax.swing.JTree;
  8 import javax.swing.tree.DefaultMutableTreeNode;
  9 import javax.swing.tree.TreeSelectionModel;
 10 import javax.swing.event.TreeSelectionEvent;
 11 import javax.swing.event.TreeSelectionListener;
 12 
 13 import org.apache.hadoop.hive.conf.HiveConf;
 14 import org.apache.hadoop.hive.ql.Context;
 15 import org.apache.hadoop.hive.ql.parse.ASTNode;
 16 import org.apache.hadoop.hive.ql.parse.ParseDriver;
 17 import org.apache.hadoop.hive.ql.parse.ParseException;
 18 import org.apache.hadoop.hive.ql.parse.ParseUtils;
 19 import org.apache.hadoop.hive.ql.session.SessionState;
 20 
 21 import com.baidu.rigel.hive.parse.CommondDemo;
 22 
 23 import java.net.URL;
 24 import java.io.IOException;
 25 import java.awt.Dimension;
 26 import java.awt.GridLayout;
 27 
 28 public class TreeDemo extends JPanel
 29                       implements TreeSelectionListener {
 30     private JEditorPane htmlPane;
 31     private JTree tree;
 32     private URL helpURL;
 33     private static boolean DEBUG = false;
 34 
 35     //Optionally play with line styles.  Possible values are
 36     //"Angled" (the default), "Horizontal", and "None".
 37     private static boolean playWithLineStyle = false;
 38     private static String lineStyle = "Horizontal";
 39     
 40     //Optionally set the look and feel.
 41     private static boolean useSystemLookAndFeel = false;
 42 
 43     public TreeDemo() {
 44         super(new GridLayout(1,0));
 45         
 46         DefaultMutableTreeNode top =null ;
 47         
 48         try {
 49             HiveConf hiveConf = new HiveConf(SessionState.class);
 50             Context ctx = new Context(hiveConf);
 51             ctx.setTryCount(10);
 52             ctx.setCmd(CommondDemo.command3);
 53             ctx.setHDFSCleanup(true);
 54             ParseDriver pd = new ParseDriver();
 55             ASTNode astTree = pd.parse(CommondDemo.command3, ctx);
 56             astTree = ParseUtils.findRootNonNullToken(astTree);
 57             top=createNodes(astTree);
 58         } catch (Exception e1) {
 59             // TODO Auto-generated catch block
 60             e1.printStackTrace();
 61         }
 62 
 63         //Create a tree that allows one selection at a time.
 64         tree = new JTree(top);
 65         tree.getSelectionModel().setSelectionMode
 66                 (TreeSelectionModel.SINGLE_TREE_SELECTION);
 67 
 68         //Listen for when the selection changes.
 69         tree.addTreeSelectionListener(this);
 70 
 71         if (playWithLineStyle) {
 72             System.out.println("line style = " + lineStyle);
 73             tree.putClientProperty("JTree.lineStyle", lineStyle);
 74         }
 75 
 76         //Create the scroll pane and add the tree to it. 
 77         JScrollPane treeView = new JScrollPane(tree);
 78 
 79         //Create the HTML viewing pane.
 80         htmlPane = new JEditorPane();
 81         htmlPane.setEditable(false);
 82         initHelp();
 83         JScrollPane htmlView = new JScrollPane(htmlPane);
 84 
 85         //Add the scroll panes to a split pane.
 86         JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
 87         splitPane.setTopComponent(treeView);
 88         splitPane.setBottomComponent(htmlView);
 89 
 90         Dimension minimumSize = new Dimension(100, 50);
 91         htmlView.setMinimumSize(minimumSize);
 92         treeView.setMinimumSize(minimumSize);
 93         splitPane.setDividerLocation(100); 
 94         splitPane.setPreferredSize(new Dimension(500, 300));
 95 
 96         //Add the split pane to this panel.
 97         add(splitPane);
 98     }
 99 
100     /** Required by TreeSelectionListener interface. */
101     public void valueChanged(TreeSelectionEvent e) {
102         DefaultMutableTreeNode node = (DefaultMutableTreeNode)
103                            tree.getLastSelectedPathComponent();
104 
105         if (node == null) return;
106 
107         Object nodeInfo = node.getUserObject();
108         if (node.isLeaf()) {
109             BookInfo book = (BookInfo)nodeInfo;
110             displayURL(book.bookURL);
111             if (DEBUG) {
112                 System.out.print(book.bookURL + ":  \n    ");
113             }
114         } else {
115             displayURL(helpURL); 
116         }
117         if (DEBUG) {
118             System.out.println(nodeInfo.toString());
119         }
120     }
121 
122     private class BookInfo {
123         public String bookName;
124         public URL bookURL;
125 
126         public BookInfo(String book, String filename) {
127             bookName = book;
128             bookURL = getClass().getResource(filename);
129             if (bookURL == null) {
130                 System.err.println("Couldn't find file: "
131                                    + filename);
132             }
133         }
134 
135         public String toString() {
136             return bookName;
137         }
138     }
139 
140     private void initHelp() {
141         String s = "TreeDemoHelp.html";
142         helpURL = getClass().getResource(s);
143         if (helpURL == null) {
144             System.err.println("Couldn't open help file: " + s);
145         } else if (DEBUG) {
146             System.out.println("Help URL is " + helpURL);
147         }
148         displayURL(helpURL);
149     }
150 
151     private void displayURL(URL url) {
152         try {
153             if (url != null) {
154                 htmlPane.setPage(url);
155             } else { //null url
156         htmlPane.setText("File Not Found");
157                 if (DEBUG) {
158                     System.out.println("Attempted to display a null URL.");
159                 }
160             }
161         } catch (IOException e) {
162             System.err.println("Attempted to read a bad URL: " + url);
163         }
164     }
165     
166     private DefaultMutableTreeNode createNodes(ASTNode tree){
167         if(tree==null||tree.isNil())
168             return null;
169         DefaultMutableTreeNode thisNode=new DefaultMutableTreeNode(tree.toString()+":"+tree.getType());
170         for(int i=0;i<tree.getChildCount();i++){
171             ASTNode child=(ASTNode)tree.getChild(i);
172             DefaultMutableTreeNode childNode=createNodes(child);
173             if(childNode!=null)
174                 thisNode.add(childNode);
175         }
176         return thisNode ;
177     }
178     /**
179      * Create the GUI and show it.  For thread safety,
180      * this method should be invoked from the
181      * event dispatch thread.
182      */
183     private static void createAndShowGUI() {
184         if (useSystemLookAndFeel) {
185             try {
186                 UIManager.setLookAndFeel(
187                     UIManager.getSystemLookAndFeelClassName());
188             } catch (Exception e) {
189                 System.err.println("Couldn't use system look and feel.");
190             }
191         }
192 
193         //Create and set up the window.
194         JFrame frame = new JFrame("TreeDemo");
195         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
196 
197         //Add content to the window.
198         frame.add(new TreeDemo());
199 
200         //Display the window.
201         frame.pack();
202         frame.setVisible(true);
203     }
204 
205     public static void main(String[] args) {
206         //Schedule a job for the event dispatch thread:
207         //creating and showing this application's GUI.
208         javax.swing.SwingUtilities.invokeLater(new Runnable() {
209             public void run() {
210                 createAndShowGUI();
211             }
212         });
213     }
214 }

使用JTreeast的结果封装了一下,可以查看相应的树形结构。

 

3.      语义解析

BaseSemanticAnalyzer.analyze-àSemanticAnalyzer. analyzeInternal ,这个方法中其实做了语义解析、逻辑执行计划、optimizer、物理执行计划这些操作都是这这个方法中运行的。

一个hql语句唯一有可能有嵌套的部分就是from后面的东东(lateview也可以认为是from后面的),这种可以使用递归的方式处理,在最上层的hql中不必关系嵌套内的东东(事实上hive的处理对于每个最上层/嵌套结构最终对外暴露都是一个最上层operator,这个operator可以对外提供一行行的数据),其它都是平行的结构,语义解析的过程可以看作是寻找一个hql的这些部分,这个主要是在看SemanticAnalyzer.doPhase1()方法中做的 。

Insclause-$num的概念 ,因为hive支持这样的语句 : 

       from    tab_from 

       insert   tab_in1   select ****

       insert   tab_in2   select ****

       insert   tab_in3   select ****

所以在一次解析中有一个from table ,但是会有多组(insert***select *** where***group by****),这个每组叫一个insclause,编号从0开始,依次增加,在TOK_INSERT节点下是平行的,每组insclause都是先TOK_DESTINATION节点,然后再跟着相应的其它节点,所以doPhase1方法才会出现一下这样的解析语句:

表示遇到了一个新的insclause,计数+1 

      解析的信息主要存储在QB以及其的一个属性QBParseInfo中 ,主要的解析信息如下:

1)       case HiveParser.TOK_SELECTDI:抽取insclause中的select distinct信息

2)       case HiveParser.TOK_SELECT:抽取select中的selExp信息(设置到QBParseInfo.destToSelExpr)、select中的agg函数信息(设置到QBParseInfo.destToAggregationExprs)、select节点中hintmapjoin之类的,设置到QBParseInfo.hints中)信息并设置到QBParseInfo中相应的字段里面

3)       case HiveParser.TOK_WHERE:抽取where后面的filter信息,设置到QBParseInfo.destToWhereExpr  

4)       case HiveParser.TOK_DESTINATION:抽取这层hqldest节点,存储到QBParseInfo.nameToDest中,由于每个dest是一组insclause的开始,所以case到这个会将insclause的计数器+1

5)       case HiveParser.TOK_FROM:针对数据源节点的处理,可以分为几种:直接的table ref,设置到QB.aliasToTabs中,别名-àtable映射 ;针对只查询subquery,针对这个子查询递归调用doPhase1,然后得到子查询的QBExpr(其有两个属性,一个是这个子查询的别名alias,一个是这个子查询的QB信息,针对于uninon操作有两个QB对象);针对lateralView,这个暂时没有细看 ;针对join操作,由于无论几轮的join最终都会转成嵌套的jioin结构,所以会针对joinleftright分别处理,如果是tabletable处理相同,如果是subquerysubquery相同 。

6)       case HiveParser.TOK_CLUSTERBY:处理cluster by,设置到QBParseInfo.destToClusterby中 。

7)       HiveParser.TOK_DISTRIBUTEBY:处理distribute by属性,同上

8)       HiveParser.TOK_SORTBYHiveParser.TOK_ORDERBY:同上,设置相应的操作属性,同样设置到QBParseInfo中。

9)       HiveParser.TOK_GROUPBY***:设置group by节点的属性,也是设置到QBParseInfo中。

10)     case HiveParser.TOK_HAVINGHiveParser.TOK_LIMIT:同上,设置每个insclause的这两个属性 。

11)     待补充

整个doPhase1就是设置这些属性的一个遍历 。

getMetaData(),获取元数据

获取source table的元数据,如果一个table实际上是一个view,将其重写为view的定义;

递归的为每个子查询中的源表获得元数据;

获取所有destination table/dir/local dir的元数据;

关于字段级别的血缘关系的一点感想

至此,整个hive的语义解析部分就完成了,在这个基础上是可以获取字段级别的血缘关系的,对于一个基本的select a1 ,a2,udf1(a3) from table/subquery  alias  where  ***  group by  c5 sort/order **  limit 10 ,这样的语句,影响select 后面的列(如果要是有insert table的话,就把select后面的和tableschema对应起来),只有udf表达式、tablegroup by后面的字段 ,对于使用agg聚合函数的则group by后面的列比较关键,否则就是from where了 。

对于第一层sql,可以先计算出select后的每个列和alias的血缘关系,然后再对alias递归做相应的操作,然后再一步步的替换到原始的table 

4.      逻辑执行计划

通过SemanticAnalyzer.genPlan(QB qb)方法获得的执行计划,QB是上一步语义解析填充的对象,该对象返回的结果是逻辑执行计划最后一步的operator 

关于operator

operator就是具体的操作,其中有可调用处理数据行的processOp(Object row, int tag)方法,已经可以被调用来处理数据的,每个operator包含childOperatorsparentOperators,根据这些可以构造operatorDAG属性结构,由于每个operator会有多个parent,所以tag来标志哪个parent传来的数据,每个operator中还有一个RowSchema属性,表明这个operator产出的数据的schemaRowSchema不仅包含正常列还包含partitions列,在hive0.11后还支持虚拟列的概念,对于TableScan操作,有filenamefileoffset这些虚拟列可以用作一些特殊用途(比如过滤table数据文件夹中不需要的文件) ,每个operator都一个OperatorDesc,相当于这个operator的配置文件,由于operator不同,所以配置也需要相应自己的实现 ,里面保留着每个operator操作时候需要的信息。

由于operator是一个dag树的概念,每个operator通过processOp(Object row, int tag)方法来处理自己的数据,处理完以后调用forward(Object row, ObjectInspector rowInspector),来将处理好的数据传递给子operator继续处理,其实forward中就是顺序调用childprocessOp方法,tag根据自己在childparent中的index来计算的,整个数据的处理就是这样的一个递归过程,对于suquery/lateview/join等子操作都是最终对外暴露一个operator,将数据传递给外层的,即外层是这个operator的子operator 

整个SemanticAnalyzer.genPlan(QB qb)后,将operatordag树的rootOperator存储在SemanticAnalyzer.topOps中,是整个DAG树的root operator ,从这几个开始可以遍历整个operator树 ,这里因为topOps需要存储整个sql包括subquery这里面的root operator,所以只能存储在全局变量中,而不能在每个QB中 。

genplan的流程

a)       生成from后面的数据源的operator,如果是table则直接生成TableScanOperator,是子查询则生成子查询的Operator ,这里面的operator是子查询的最后一个operator,为当前提供row数据的。

b)       对于PTFwinfun等流程暂未详细看 。

c)       针对join的处理,如果是from后面有join操作的话,则上一步中会生成两个operator,一个是joinleft以及是joinright的,这一步会根据QB中的joinTree信息,将这两部分组合起来生成一个operator,作为提供数据源的operator,对于join的处理会涉及到join操作的合并,但是没有考虑mapjoinmapjoin是在后面的逻辑计划optimizer中做的 。

d)       genBodyPlan(),先生成where条件的filteroperator,然后在filter operator的基础生成group byoperator,处理group by时候会根据是否处理倾斜、是否mapside-join、是否合并多个insclausegroup by等情况生成不同的group by operator,这个operator是上一步的child operator 

e)       然后在group by operator之后,在genPostGroupByBodyPlan()方法中依次处理having(生成一个新的filterOperator)、Select操作、sort/order bylimitFileSinkOperator操作,下一个为上一个的子节点 。

f)       Demo如下  TS[0]-->FIL[1]-->SEL[2]-->GBY[3]-->RS[4]-->GBY[5]-->SEL[6]-->RS[7]-->EX[8]-->LIM[9]-->RS[10]-->EX[11]-->LIM[12]-->FS[13] ,数字为每个operatorid

Operator查看工具

1、   因为SemanticAnalyzer.topOpsprivate变量,所以需要改动一些相应类属性的访问权限才能使用

2、   注释掉物理执行计划的代码,生成物理执行计划的时候会破坏SemanticAnalyzer.topOps的树形结构

  1 /**
  2  * This application that requires the following additional files:
  3  *   TreeDemoHelp.html
  4  *    arnold.html
  5  *    bloch.html
  6  *    chan.html
  7  *    jls.html
  8  *    swingtutorial.html
  9  *    tutorial.html
 10  *    tutorialcont.html
 11  *    vm.html
 12  */
 13 import javax.swing.JEditorPane;
 14 import javax.swing.JFrame;
 15 import javax.swing.JPanel;
 16 import javax.swing.JScrollPane;
 17 import javax.swing.JSplitPane;
 18 import javax.swing.UIManager;
 19 import javax.swing.JTree;
 20 import javax.swing.tree.DefaultMutableTreeNode;
 21 import javax.swing.tree.TreeSelectionModel;
 22 import javax.swing.event.TreeSelectionEvent;
 23 import javax.swing.event.TreeSelectionListener;
 24 
 25 import org.apache.hadoop.hive.conf.HiveConf;
 26 import org.apache.hadoop.hive.ql.Context;
 27 import org.apache.hadoop.hive.ql.exec.Operator;
 28 import org.apache.hadoop.hive.ql.parse.ASTNode;
 29 import org.apache.hadoop.hive.ql.parse.ParseDriver;
 30 import org.apache.hadoop.hive.ql.parse.ParseException;
 31 import org.apache.hadoop.hive.ql.parse.ParseUtils;
 32 import org.apache.hadoop.hive.ql.parse.SemanticAnalyzer;
 33 import org.apache.hadoop.hive.ql.parse.SemanticAnalyzerFactory;
 34 import org.apache.hadoop.hive.ql.plan.OperatorDesc;
 35 import org.apache.hadoop.hive.ql.session.SessionState;
 36 
 37 import com.baidu.rigel.hive.parse.CommondDemo;
 38 
 39 import java.net.URL;
 40 import java.util.ArrayList;
 41 import java.util.Collection;
 42 import java.util.HashSet;
 43 import java.util.Iterator;
 44 import java.util.List;
 45 import java.util.Set;
 46 import java.io.IOException;
 47 import java.awt.Dimension;
 48 import java.awt.GridLayout;
 49 
 50 public class OpetatorTreeDemo extends JPanel
 51                       implements TreeSelectionListener {
 52     private JEditorPane htmlPane;
 53     private JTree tree;
 54     private URL helpURL;
 55     private static boolean DEBUG = false;
 56 
 57     //Optionally play with line styles.  Possible values are
 58     //"Angled" (the default), "Horizontal", and "None".
 59     private static boolean playWithLineStyle = false;
 60     private static String lineStyle = "Horizontal";
 61     
 62     //Optionally set the look and feel.
 63     private static boolean useSystemLookAndFeel = false;
 64 
 65     public OpetatorTreeDemo() {
 66         super(new GridLayout(1,0));
 67         
 68         DefaultMutableTreeNode top =null ;
 69         
 70         try {
 71             ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
 72             System.out.println( classLoader.getResource("hive-default.xml"));
 73             System.out.println( classLoader.getResource("hive-site.xml"));
 74             HiveConf hiveConf = new HiveConf(SessionState.class);
 75             SessionState.start(new SessionState(hiveConf));
 76             Context ctx = new Context(hiveConf);
 77             ctx.setTryCount(10);
 78             ctx.setCmd(CommondDemo.command3);
 79             ctx.setHDFSCleanup(true);
 80             ParseDriver pd = new ParseDriver();
 81             ASTNode astTree = pd.parse(CommondDemo.command3, ctx);
 82             astTree = ParseUtils.findRootNonNullToken(astTree);
 83             SemanticAnalyzer sem =(SemanticAnalyzer)SemanticAnalyzerFactory.get(hiveConf, astTree);
 84             sem.analyze(astTree, ctx);
 85             sem.validate();        
 86             List<Operator<? extends OperatorDesc>>  topOpList=new ArrayList<Operator<? extends OperatorDesc>>(sem.topOps.values());
 87             if(topOpList.size()==1){
 88                 top=createNodes(topOpList.get(0));
 89             }else {
 90                 top=new DefaultMutableTreeNode("root");
 91                 for(Operator<? extends OperatorDesc> op:topOpList){
 92                     top.add(createNodes(op));
 93                 }
 94             }
 95         } catch (Exception e1) {
 96             // TODO Auto-generated catch block
 97             e1.printStackTrace();
 98         }
 99 
100         //Create a tree that allows one selection at a time.
101         tree = new JTree(top);
102         tree.getSelectionModel().setSelectionMode
103                 (TreeSelectionModel.SINGLE_TREE_SELECTION);
104 
105         //Listen for when the selection changes.
106         tree.addTreeSelectionListener(this);
107 
108         if (playWithLineStyle) {
109             System.out.println("line style = " + lineStyle);
110             tree.putClientProperty("JTree.lineStyle", lineStyle);
111         }
112 
113         //Create the scroll pane and add the tree to it. 
114         JScrollPane treeView = new JScrollPane(tree);
115 
116         //Create the HTML viewing pane.
117         htmlPane = new JEditorPane();
118         htmlPane.setEditable(false);
119         initHelp();
120         JScrollPane htmlView = new JScrollPane(htmlPane);
121 
122         //Add the scroll panes to a split pane.
123         JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
124         splitPane.setTopComponent(treeView);
125         splitPane.setBottomComponent(htmlView);
126 
127         Dimension minimumSize = new Dimension(100, 50);
128         htmlView.setMinimumSize(minimumSize);
129         treeView.setMinimumSize(minimumSize);
130         splitPane.setDividerLocation(100); 
131         splitPane.setPreferredSize(new Dimension(500, 300));
132 
133         //Add the split pane to this panel.
134         add(splitPane);
135     }
136 
137     /** Required by TreeSelectionListener interface. */
138     public void valueChanged(TreeSelectionEvent e) {
139         DefaultMutableTreeNode node = (DefaultMutableTreeNode)
140                            tree.getLastSelectedPathComponent();
141 
142         if (node == null) return;
143 
144         Object nodeInfo = node.getUserObject();
145         if (node.isLeaf()) {
146             BookInfo book = (BookInfo)nodeInfo;
147             displayURL(book.bookURL);
148             if (DEBUG) {
149                 System.out.print(book.bookURL + ":  \n    ");
150             }
151         } else {
152             displayURL(helpURL); 
153         }
154         if (DEBUG) {
155             System.out.println(nodeInfo.toString());
156         }
157     }
158 
159     private class BookInfo {
160         public String bookName;
161         public URL bookURL;
162 
163         public BookInfo(String book, String filename) {
164             bookName = book;
165             bookURL = getClass().getResource(filename);
166             if (bookURL == null) {
167                 System.err.println("Couldn't find file: "
168                                    + filename);
169             }
170         }
171 
172         public String toString() {
173             return bookName;
174         }
175     }
176 
177     private void initHelp() {
178         String s = "TreeDemoHelp.html";
179         helpURL = getClass().getResource(s);
180         if (helpURL == null) {
181             System.err.println("Couldn't open help file: " + s);
182         } else if (DEBUG) {
183             System.out.println("Help URL is " + helpURL);
184         }
185         displayURL(helpURL);
186     }
187 
188     private void displayURL(URL url) {
189         try {
190             if (url != null) {
191                 htmlPane.setPage(url);
192             } else { //null url
193         htmlPane.setText("File Not Found");
194                 if (DEBUG) {
195                     System.out.println("Attempted to display a null URL.");
196                 }
197             }
198         } catch (IOException e) {
199             System.err.println("Attempted to read a bad URL: " + url);
200         }
201     }
202 //    private  void getRootOperator(Collection<Operator<? extends OperatorDesc>> leafs ,Set<Operator<? extends OperatorDesc>> roots){
203 //        for(Operator<? extends OperatorDesc> op :leafs){
204 //            if(op.getParentOperators()!=null&&!op.getParentOperators().isEmpty())
205 //                getRootOperator(op.getParentOperators(),roots);
206 //            else
207 //                roots.add(op);
208 //        }
209 //    }
210     private DefaultMutableTreeNode createNodes( Operator<? extends OperatorDesc> tree){
211         if(tree==null)
212             return null;
213         DefaultMutableTreeNode thisNode=new DefaultMutableTreeNode(tree.toString()+":"+tree.getConf().getClass().getSimpleName());
214         List<Operator<? extends OperatorDesc>> childer=tree.getChildOperators();
215         if(childer==null)
216             return thisNode ;
217         for(Operator<? extends OperatorDesc> child:childer){
218             DefaultMutableTreeNode childNode=createNodes(child);
219             if(childNode!=null)
220                 thisNode.add(childNode);
221         }
222         return thisNode ;
223     }
224     /**
225      * Create the GUI and show it.  For thread safety,
226      * this method should be invoked from the
227      * event dispatch thread.
228      */
229     private static void createAndShowGUI() {
230         if (useSystemLookAndFeel) {
231             try {
232                 UIManager.setLookAndFeel(
233                     UIManager.getSystemLookAndFeelClassName());
234             } catch (Exception e) {
235                 System.err.println("Couldn't use system look and feel.");
236             }
237         }
238 
239         //Create and set up the window.
240         JFrame frame = new JFrame("TreeDemo");
241         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
242 
243         //Add content to the window.
244         frame.add(new OpetatorTreeDemo());
245 
246         //Display the window.
247         frame.pack();
248         frame.setVisible(true);
249     }
250 
251     public static void main(String[] args) {
252         //Schedule a job for the event dispatch thread:
253         //creating and showing this application's GUI.
254         javax.swing.SwingUtilities.invokeLater(new Runnable() {
255             public void run() {
256                 createAndShowGUI();
257             }
258         });
259     }
260 }

 

常用operator以及作用(转自别人文档)

OperatorFactory是算子工厂,该工厂根据一个算子描述实例生成相应的算子实例。目前注册的算子及其描述包括以下这些。

 

 

 

 

5.      Optimizer优化逻辑执行计划

Optimizer的初始化

根据配置添加一大推的Transform实现进去(一个Transform相当于一个优化器),优化operator时候,顺序调用Transform. Transformpctx)就可以啦

对于一个Transform的执行流程

对于每一个Transform其实都是在内部维护了一系列的规则Rule到优化器NodeProcessor的映射map,例如UnionProcessor

Rule:一个匹配的字符串+处理process

GraphWalker是一个对于树结构的遍历器,这个实现为前序遍历器,Dispatcher disp中封装的是那些规则à优化处理器的Map ,方法调用:GraphWalker.startWalking—后续遍历执行计划树à针对每个节点调用Dispatcher. Dispatch方法  

其中Dispatcher. dispatch(Node nd, Stack<Node> stack, Object... nodeOutputs)方法,是针对树上的某个节点进行处理(其实就是规则匹配,使用匹配到的优化器处理一遍),他的几个参数释义如下:

Node nd:待处理的节点

Stack<Node> stack:该节点的前置节点,因为是后序遍历,所以stack中是这个节点的一层层父节点 。

Object[]nodeOutputs:是每个子节点经过优化器处理后的结果,一般都是null  ,以下是DefaultRuleDispatcher. Dispatch方法  

对于Rulecost方法,代码如下:

返回的值是匹配的字符串的长度,即将前置父节点按照辈分打成字符串,然后和每条规则Rule进行字符串匹配,然后返回匹配结果字符串长度,选择cost最大的即是选择匹配最长字符串的规则 。

操作的name类似于这样的字符串:

TS  : TableScan  Operator 

FLT : filter 操作  

Hive中一些基于规则的optimizer

1.       Generator

处理字段以及表级别血缘关系的优化器,本身只是提取数据的

2.       PredicateTransitivePropagate

针对的操作流:JOINàRSàFIL ,join完后,写文件,然后再过滤 

过滤条件提前到RS之前

3.       PredicatePushDown

谓词下推 

4.       PartitionPruner

Partition裁剪,将partitions条件放在

5.       PartitionConditionRemover

移除partitions条件 

6.       ListBucketingPruner

对于bucketTS裁剪,不是很明白  

7.       ColumnPruner

列裁剪 ,通过血缘关系,修改Operator中的RowResolver对象

8.       SkewJoinOptimizer

针对数据倾斜的处理,将倾斜数据和非倾斜数据分开join,然后再union 

修改后

9.       RewriteGBUsingIndex

针对有index的表,做的聚合函数优化,such as 

10.     GroupByOptimizer

针对sorted &&  bukect的表做group by 的优化  ,聚合可以完全在map端完成 

11.     SamplePruner

Samplesmaple下放到TS的操作上 

12.     MapJoinProcessor

Join操作转换成mapjoin的操作 

13.     BucketMapJoinOptimizer

针对join表示bucket的表做的bucketmapjoin

14.     SortedMergeBucketMapJoinOptimizer

Bucketsortmapjoin,不许用本地hashTable的操作了 

15.     BucketingSortingReduceSinkOptimizer

针对bucket sorting 的数据,节省一轮sortreduce 

16.     UnionProcessor

只是识别是否union的两个subquery都是只需要在map就可以完成,这个只是存储这个信息,不对operator做任何操作 。

17.     JoinReorder

针对join时候,小表放左边来做的优化,暂时hive只是通过标记的形式(貌似是使用streamhint来标记的),后续这块可以是实现为基于cost的优化

18.     ReduceSinkDeDuplication

如果两个RS是相同的,则合并这两个文件的吐出  

19.     NonBlockingOpDeDupProc

合并连续的sel/sel 以及filter/filter这样的操作 

20.     GlobalLimitOptimizer

简单的将limit N的操作下推到具体的TS操作中 

21.     CorrelationOptimizer

一整篇优化的论文 

http://www.cse.ohio-state.edu/hpcs/WWW/HTML/publications/papers/TR-11-7.pdf

 

22.     LimitPushdownOptimizer

针对limit的下推操作,将limit N的操作转化为top-N的操作,可以在reducesort的基础上做优化,本质上应该是采用topN的堆算法   

 

23.     SimpleFetchOptimizer

就是针对直接取数的select  * from  table  这样的,直接转化成本地的local的一个fetchTask

24.     SimpleFetchAggregation

将最后的一些agg操作放到FetchTask中来做 

6.      物理执行计划

将以上的operator翻译成一个个的可执行的task,本部分暂未了解

常用的task

7.      rootTask的执行

经过物理执行计划,会将所有的operator封装到一个个的task中,构成一个List<Task<? extends Serializable>> rootTasks ,所以root task的集合, task是和operator差不多的数据结构,每个task有自己的parentTask List以及childTask List,并且task有自己的executeTask()方法,可以运行 ,具体的执行过程在Driver.Execute()方法中,具体如下 :

1、    初始化一个runnableQueue<Task<? extends Serializable>> tasktaskResult对应的map,将rootTasks加入到queue中。

2、    依次从queue中取出task,运行,运行成功后,看其子task是否满足运行条件(即所有parentTask都执行完毕),如果ok,则将相应的子task加入可运行的队列中 。

3、    重复步骤2,直到所有的task运行完毕 。

8.      Tips

以上是整个hive运行的主流程:hql解析成语法树-语法解析-语义解析-逻辑执行计划-optimizer-物理执行计划—task执行 ,以下是看代码时候的一些笔记tips

1)       关于TOK_SELEXPR类型的节点 

 

对于TOK_SELEXPR来说,如果有别名的话,有两个子节点,第二个子节点是别名,第一个是表达式  

2)       BaseSemanticAnalyzer.validate()  ,验证plan的结果是否是有效的

3)       关于一般的函数(udf/udaf)表达式 

 

child[0]位置上是函数名,1要计算的数值(列名称、嵌套的的fun等等)

 

4)       关于FunctionRegistry   

     管理整个hive执行过程中所有的函数的,所以hive引入新的函数需要在代码中显示制定,以方便注册进来   

 

union的数据结构构成FunctionInfo信息,若是相应类型,则有值  

 

5)       TOK_INSERT_INTO 针对的是desttable(partitions)的情况  

      TOK_INSERT  dest可以是directory 或者 local directory 或者  table(partition)

     

 

以上两个分别是insert into   insert overwrite  ,后一个支持if not exits 

也可以看出,TOK_TAB其实包含两部分信息,表名和分区信息  

6)       直接对于table的引用结构 

7)       QBExpr  QB  QBParseInfo 依次为包含关系

           QBExpr qbexpr = new QBExpr(alias);

           QB qb1=qbexpr.getQB();

           QBParseInfo qbParse1=qb1.getParseInfo();

每个QBExpr中可以有两个QB,用于处理union的操作 

每一个子查询TOK_QUERY对应这一个QBExpr  ,可以看到QBExpr 的构造方法对应的就是TOK_QUERY的别名,每个TOK_QUERY必须有别名 。

        

8)       整个AST树以TOK_QUERY为基本单位   ,所有的子查询都会重写为  

     insert into  tmp_file   select *** from **** where  ****   

9)       关于hql中的join   

     

hql中的joinast树这种嵌套的结构,a join b join c  join d 会翻译成:join (join (join(join a b) c) d) 这种嵌套的join形式   

10)     对于join 操作  

0  1 位置是两个表,2未知是on条件        

对于多个join操作合并成left嵌套的操作    

 

11)     关于hiveTOK_QUERY语句  

 

SELECT [/*mapjoin(****)*/]  

[ALL | DISTINCT] select_expr, select_expr, ... 
FROM table_reference 
[WHERE where_condition] 
[GROUP BY col_list] 
[ CLUSTER BY col_list | [DISTRIBUTE BY col_list] [SORT BY col_list] ] 
[LIMIT number] 

 

转换以后就是  

FROM  table_reference/tabjoin/tabunion  

INSERT INTO   tabl[partspec] /local directory /directory  

SELECT [/*mapjoin(****)*/]  

[ALL | DISTINCT] select_expr, select_expr, ... 

[WHERE where_condition]  

[GROUP BY col_list] 

[ CLUSTER BY col_list | [DISTRIBUTE BY col_list] [SORT BY col_list] ]  [ORDER BY col_list] ]  
[LIMIT number] 

 

包含的数据项是:from  insert select  where  group by   havingclusterdistribute sort by  order by limit  11种元素   

 

 

insert overwrite directory '/home/work/data/tmp'

select

contract_line_id,st_date,pdate,sum(alb_cust_id)

from analyse_db.contline_revenue_day_cut

where pdate='2013-11-12' and prodline_id>10

group by contract_line_id,st_date,pdate

sort by contract_line_id

limit 10  

12)     doPhase1中的case    

        对于TOK_QUERYast树进行遍历,一个tok_query中的以上几个组成部分,对于子查询(所有的子查询都重写为一个新的tok_query类型的ast树)等,都是递归再处理   

       这对几种元素做的处理以及存储信息(主要是在QBQBParseInfo中):

         1 HiveParser.TOK_FROM  

      一层的sql ast中有且只有一个TOK_FROM  类型的节点,但是TOK_FROM 下面的可以是一个 子查询/union/table/join操作,在这个case语句中

        如果是table,则向QBParseInfo.QBParseInfoput一个映射关系,keytable的别名,valuetableast  

        如果是subquery,则向QB.aliasToSubq中设置一个key-valuekey为子查询别名,value为该子查询的QBExpr

        如果是joi操作的话,则将joinleftright分别取出来,执行上面两个操作 ,两个中可能都有值或者是某一个中有两个值 。

        如果是lateview的话,**********

13)     阅读源代码的几个经验

        一定要单机debug 

        对于太琐碎/不常用(例如ptflateview)的分支可以先放弃

14)     解析中Clause的概念 

       hive支持这样的语句 

       from    tab_from 

       insert   tab_in1   select ****

       insert   tab_in2   select ****

       insert   tab_in3   select ****

一个insert组就称为一个Clause ,其key  name一次为 insClause-0 insClause-1 insClause-2 ........

就算是正常insert ***   select ****   from  ***   ,也会给改写成上面的形式,只是只有一个clause name=insClause-0  

 

15)     TOK_SELECTDI   (和 TOK_SELECT 并列的)

        针对这种   select   distinct  a ,b,c  from   tab  (相当于select a,b,c from tab group by a,b,c  )

16)     针对于上面那种多个insert的操作  

        group  by  相同的找出来,在tabScan的自己点中添加自己的where过滤filter  

17)     OpParseContext  

         每个operator的上下文,存储在全局的opParseCtx这个map中,key是相应的operator  ,主要是保存operator的输出SchemaRowResolver  

18)     关于ExplainSemanticAnalyzer

其实是将ast树去掉   Explain节点,然后再

SemanticAnalyzerFactory.get(conf, (ASTNode) ast .getChild(0));得到真正的SemanticAnalyzer,然后再调用相应的analyzer方法,得到优化后的逻辑计划(记住此处不是物理执行计划),然后再打印  

9.      待补充

转载于:https://www.cnblogs.com/serendipity/articles/3738158.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值