【软件工程实践】Hive研究-Blog2
2021SC@SDUSC
研究内容简略介绍
本人负责的是负责的是将查询块QB转换成逻辑查询计划(OP Tree)
如下的代码出自apaceh-hive-3.1.2-src/ql/src/java/org/apache/hadoop/hive/ql/plan中,也就是我的分析目标代码。由于Blog1已经研究了mapper文件夹下的第一个文件CachingStatsSource.java文件,那么我们这周就来研究该文件夹下的剩余源码。
EmptyStatsSource.java文件代码解析
我们首先附上整个java文件的源码。
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hive.ql.plan.mapper;
import java.util.Map;
import java.util.Optional;
import org.apache.hadoop.hive.ql.optimizer.signature.OpTreeSignature;
import org.apache.hadoop.hive.ql.stats.OperatorStats;
public final class EmptyStatsSource implements StatsSource {
public static StatsSource INSTANCE = new EmptyStatsSource();
private EmptyStatsSource() {
}
@Override
public boolean canProvideStatsFor(Class<?> class1) {
return false;
}
@Override
public Optional<OperatorStats> lookup(OpTreeSignature treeSig) {
return Optional.empty();
}
@Override
public void putAll(Map<OpTreeSignature, OperatorStats> map) {
throw new RuntimeException("This is an empty source!");
}
}
开始解析
方法EmptyStatsSource
private EmptyStatsSource() {
}
显然,这是一个构造类方法,而方法体中并不带有任何参数,这是一个空的构造方法,没有特别之处。
方法canProvideStatsFor
@Override
public boolean canProvideStatsFor(Class<?> class1) {
return false;
}
我们先来看一下这个参数class<?> class1是什么类型的参数。由Blog1的学习可知,这个class<?>类型的参数就是代表着一个类class,比如说当前类或者父类。那么这个方法无论输入的是什么参数,它的返回值都是false。该方法的名字canProvideStatsFor,翻译成中文就是可以为···提供数据,也就是返回一个false,不过我们暂时还不清楚该方法的具体作用是什么,如果只是单单返回一个false的话,那为什么需要参数呢?这样做的话不是更加麻烦么,是否在调用该函数的部分直接填上flase就可以了呢?我们可以大胆的猜测,该方法是内部调用返回的false方法,在外部调用时请求该方法必须要带一个参数,不能直接使用false,这也就避免了异常的发生,例如当没有这个参数却想要错误的调用false时,就不能成功调用方法,从而提醒代码开发人员进行参数的校验补充。
方法lookup
@Override
public Optional<OperatorStats> lookup(OpTreeSignature treeSig) {
return Optional.empty();
}
在这个方法中,对于参数treesig在方法体中并未使用,故我们先忽略该参数,重点关注方法Optional.empty()方法。经过查阅资料,我们得到了Oracle官方对该方法的注释:
public static <T> Optional<T> empty()
Returns an empty Optional instance. No value is present for this Optional.
API Note:
Though it may be tempting to do so, avoid testing if an object is empty by comparing with == against instances returned by Optional.empty(). There is no guarantee that it is a singleton. Instead, use isPresent().
Type Parameters:
T - The type of the non-existent value
Returns:
an empty Optional
那么这个方法的作用就是返回一个空指针。按理来说,该方法应该用来比对一个对象是否为空,但是该源码直接让他返回了空值,我们猜测这是一个封装的内部方法,他的作用就是返回一个封装的空对象,在外部调用时进行判断,那么这也就可以解释既然返回的是空指针,为什么还需要一个参数的原因了,同上文的canProvideStatsFor方法,也许是为了保证外部调用的参数完整性。我们可以来看一个关于Optional.empty()方法的用例:
// Java program to demonstrate
// Optional.empty() method
import java.util.*;
public class GFG {
public static void main(String[] args)
{
// create a Optional
Optional<Integer> op
= Optional.empty();
// print value
System.out.println("Optional: "
+ op);
}
}
输出:
Optional: Optional.empty
我们再来关注一下方法中所需要带上的参数,一个类型为OpTreeSignature类。如下是在apache官网API上查阅的资料:
从官方注释上来看这个类可以调用一些重写过的方法,但是在这个方法中没有体现出具体的作用,我们猜测也是在外部调用时必须要有此类参数的作用,用于校验参数完整,以防错误的调用而引发异常。
方法putAll
@Override
public void putAll(Map<OpTreeSignature, OperatorStats> map) {
throw new RuntimeException("This is an empty source!");
同样的,我们对需要输入的参数类型为Map<OpTreeSignature, OperatorStats>的那个参数不做过多的解读,暂时认为是要求调用方的参数完整防止错误调用。我们重点关注后续的语句throw new RuntimeException(“This is an empty source!”);
我们先来关注打印的内容:This is an empty source ,翻译成中文的意思为“这是一个空的源资源!”,而throw new RuntimeException方法,我们查阅资料得到了如下解释:
try{
....
}catch(Exception e){
e.printStackTrace( );
throw new RuntimeException(e);
//throw new RuntimeException(e.getMessage());
}
这是处理没法进一步处理的异常的一般做法。try块中出现了一个异常,它被catch住了,我们首先要在标准输出上打印出异常但是如果没有throw这句,这个错误就静悄悄地被catch块吃掉了,程序会继续运行。 throw new RuntimeException就是要把异常继续抛出,要么由上层方法解决,要么会终止程序运行,比如这里,如果初始化都无法正确完成,再继续运行下去也没有必要了。至于说多打印一句话,还是因为在工具环境下,你比较关注控制台,实际部署环境,没人看控制台信息,都会去看日志中记录的异常信息。 有结束进程的作用。try catch如果不写throw new RuntimeException 只是不执行本方法后面的代码,然后跳出本方法后继续执行其他方法,不会结束程序。 如果在其他应用中,还可以把异常抛给上层调用者来处理。那么本方法的作用就是抛出一个异常,然后输出语句"This is an empty source!",是一个正常的抛出异常的方法,方便外部调用。
至此,对于EmptyStatsSource.java文件的源码解析已全部完成,我们继续往下解析源码。
GroupTransformer.java文件代码解析
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hive.ql.plan.mapper;
import org.apache.hadoop.hive.ql.plan.mapper.PlanMapper.EquivGroup;
public interface GroupTransformer {
void map(EquivGroup group);
}
显而易见,这是一个接口类型的java文件,由interface声明就可以看出来了。
方法map
该接口只有一个方法:map方法,返回类型为void,需要的参数为EquivGroup类型。我们不妨来查询一下EquivGroup类的具体信息。我们在Apache官网的官方API接口上找到了关于这个类的详细信息:
EquivGroup
关于介绍内容如下:
A set of objects which are representing the same thing. A Group may contain different kind of things which are connected by their purpose; For example currently a group may contain the following objects:
Operator(s) - which are doing the actual work; there might be more than one, since an optimization may replace an operator with a new one
Signature - to enable inter-plan look up of the same data
OperatorStats - collected runtime information
简单来说,就是一类相同的东西都可以放在这个类中。那么该方法的作用我猜测是传入一个组group,然后对其进行批量操作。我们接着解析。
MapBackedStatsSource.java文件代码解析
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hive.ql.plan.mapper;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.hadoop.hive.ql.exec.Operator;
import org.apache.hadoop.hive.ql.optimizer.signature.OpTreeSignature;
import org.apache.hadoop.hive.ql.stats.OperatorStats;
public class MapBackedStatsSource implements StatsSource {
private Map<OpTreeSignature, OperatorStats> map = new ConcurrentHashMap<>();
@Override
public boolean canProvideStatsFor(Class<?> clazz) {
if (Operator.class.isAssignableFrom(clazz)) {
return true;
}
return false;
}
@Override
public Optional<OperatorStats> lookup(OpTreeSignature treeSig) {
return Optional.ofNullable(map.get(treeSig));
}
@Override
public void putAll(Map<OpTreeSignature, OperatorStats> map) {
this.map.putAll(map);
}
}
方法canProvideStatsFor
@Override
public boolean canProvideStatsFor(Class<?> clazz) {
if (Operator.class.isAssignableFrom(clazz)) {
return true;
}
return false;
}
我们直接来关注一波这个方法的签名:
public native boolean isAssignableFrom(Class<?> cls);
我们再从网上查阅资料,得到了该方法在JDK中的注释:
Determines if the class or interface represented by this Class object is either the same as,
or is a superclass or superinterface of, the class or interface represented by the specified Class parameter.
It returns true if so; otherwise it returns false.
If this Class object represents a primitive type, this method returns true if the specified Class parameter is exactly this Class object; otherwise it returns false.
翻译成中文的意思就是:有两个Class类型的类象,一个是调用isAssignableFrom方法的类对象(我们称之为对象a),以及方法中作为参数的这个类对象(我们称之为对象b),这两个对象如果满足以下条件则返回true,否则返回false:
a对象所对应类信息是b对象所对应的类信息的父类或者是父接口,简单理解即a是b的父类或接口
a对象所对应类信息与b对象所对应的类信息相同,简单理解即a和b为同一个类或同一个接口
那么canProvideStatsFor方法的意思就明了了:判断传入的参数是否为Oprator.class的父类或者接口,或者同一个类,如果是则返回true,否则false。该方法应该是用来类的种类判断,以防过界的调用方法和使用参数。
方法lookup
@Override
public Optional<OperatorStats> lookup(OpTreeSignature treeSig) {
return Optional.ofNullable(map.get(treeSig));
}
这个方法我们在Blog1中已经完全解析过了,我们附上Blog1对该方法的解析:Blog1地址
以下是对该方法的解析:
方法putAll
@Override
public void putAll(Map<OpTreeSignature, OperatorStats> map) {
this.map.putAll(map);
}
看到这个方法,我觉得非常奇怪。首先@Override符号标志着这个方法是重写的方法,但是在方法体中又调用了一样名字的putAll方法,这样不就是递归调用了么?当然不是。这个putAll方法只在这个类里生效,而它调用的putAll方法是原来的putAll方法。那么我们来看一下原来的putAll方法是什么样的一个方法:
putAll() 方法将指定所有的键/值对插入到 HashMap 中。
putAll() 方法的语法为:
hashmap.putAll(Map m)
为了更加直观的了解该方法,我们使用一个示例来演示说明:
import java.util.HashMap;
class Main {
public static void main(String[] args) {
// 创建一个 HashMap
HashMap<Integer, String> sites = new HashMap<>();
// 往 HashMap 添加一些元素
sites.put(1, "Google");
sites.put(2, "Runoob");
sites.put(3, "Taobao");
System.out.println("sites HashMap: " + sites);
// 创建另一个 HashMap
HashMap<Integer, String> sites2 = new HashMap<>();
sites2.put(1, "Weibo"); // 已存在会被替换
sites2.put(4, "Wiki");
// 将所有的映射关系从 sites 添加到 sites2
sites2.putAll(sites);
System.out.println("sites2 HashMap: " + sites2);
}
}
如下是输出的结果:
sites HashMap: {1=Google, 2=Runoob, 3=Taobao}
sites2 HashMap: {1=Google, 2=Runoob, 3=Taobao, 4=Wiki}
那么对于重写的putAll方法,我们就知道了其意图:将本类的独有变量map作为调用原putAll方法的接口,将作为参数的map再作为参数传入原putAll方法中,实现上述所展示的功能,将参数map的所有键值对赋予类中的私有变量map。
小结
通过本周对Hive源码的学习,我又加深了对Hive的理解,逐渐接触到了最核心的内容了。希望下周的学习能够继续带来新发现。