【软件工程实践】Hive研究-Blog2

【软件工程实践】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的理解,逐渐接触到了最核心的内容了。希望下周的学习能够继续带来新发现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值