Hive-源码带你看hive命令背后都做了什么

一、源码下载

  下面是hive官方源码下载地址,我下载的是hive-3.1.3,那就一起来看下吧

https://dlcdn.apache.org/hive/hive-3.1.3/apache-hive-3.1.3-src.tar.gz

二、总结

由于篇幅太长担心占用你的时间,先把总结写到前面。

1、命令行输入 hive 命令 ,包括以下多种情况:

        a、hive -e "hql字符串" 

        b、hive -f hql文件

        c、hive -h

        d、hive --version

        e、hive --orcfiledump orc文件HDFS路径

        f、hive --rcfilecat  rc文件HDFS路径

        g、等等

2、初始化,包括

        a、配置相关路径

        b、hive启动脚本使用的jvm堆大小

        c、配置Hadoop、Spark、HBase等相关路径和环境变量

        d、添加distcp

        e、添加辅助jar

        f、等等

3、依次执行ext下的脚本(针对hive命令后面跟了什么样的服务)

4、执行ext/util下的脚本

5、执行默认的旧版CliDriver中main方法(cli.sh中有提到)

6、获取终端的输入并进行解析

        a、如果是 -e -f 会一次性解析并执行其中的hql

        b、如果不是会创建ConsoleReader和历史命令记录服务循环处理控制台输入的hql

三、脚本部分

注意:为了看起来清晰,会把不重要的部分省略掉

1、hive

#!/usr/bin/env bash

#......省略......

#初始化一些配置路径和hive启动脚本使用的jvm堆大小
. "$bin"/hive-config.sh

SERVICE=""
HELP=""
SKIP_HBASECP=false
SKIP_HADOOPVERSION=false

SERVICE_ARGS=()
#省略,这里我们只输入的hive,没有加其他参数
while [ $# -gt 0 ]; do
  case "$1" in
	#......省略......
  esac
done

if [ "$SERVICE" = "" ] ; then
  if [ "$HELP" = "_help" ] ; then
    SERVICE="help"
  else
	#默认走的服务是cli 即客户端
    SERVICE="cli"
  fi
fi

#beeline是 Hive 0.11版本引入的新命令行客户端工具,本篇博客先分析hive命令客户端
if [[ "$SERVICE" == "cli" && "$USE_BEELINE_FOR_HIVE_CLI" == "true" ]] ; then
  SERVICE="beeline"
fi


#conf文件夹下默认有个hive-env.sh.template 可以重命名为hive-env.sh来使用
#在此处设置HiveHive和Hadoop环境变量。这些变量可用于控制HiveHive的执行。
#管理员应该使用它来配置HiveHive安装(这样用户就不必设置环境变量或设置命令行参数来获得正确的行为)。
#正在调用的hivehive服务(CLI等)可通过环境变量SERVICE获得
if [ -f "${HIVE_CONF_DIR}/hive-env.sh" ]; then
  . "${HIVE_CONF_DIR}/hive-env.sh"
fi

if [[ -z "$SPARK_HOME" ]]
then
  bin=`dirname "$0"`
  # 现在很多hadoop集群中也按照了spark,这里加载下spark的以西配置
  if test -e $bin/../../spark; then 
    sparkHome=$(readlink -f $bin/../../spark)
    if [[ -d $sparkHome ]]
    then
      export SPARK_HOME=$sparkHome
    fi
  fi
fi

CLASSPATH="${TEZ_CONF_DIR:-/etc/tez/conf}:${HIVE_CONF_DIR}"

HIVE_LIB=${HIVE_HOME}/lib

# hive执行所必须的jar包
if [ ! -f ${HIVE_LIB}/hive-exec-*.jar ]; then
  echo "Missing Hive Execution Jar: ${HIVE_LIB}/hive-exec-*.jar"
  exit 1;
fi

if [ ! -f ${HIVE_LIB}/hive-metastore-*.jar ]; then
  echo "Missing Hive MetaStore Jar"
  exit 2;
fi

#客户端特定代码
if [ ! -f ${HIVE_LIB}/hive-cli-*.jar ]; then
  echo "Missing Hive CLI Jar"
  exit 3;
fi

# Hbase和Hadoop使用各自的log4j jar。  包括 hives log4j jar 可能会导致log4j警告
# 因此,将hives log4j-JAR保存在LOG_JAR_CLASSPATH中,并在Hbase和Hadoop调用完成后将其添加到类路径中
LOG_JAR_CLASSPATH="";

for f in ${HIVE_LIB}/*.jar; do
  if [[ $f == *"log4j"* ]]; then
    LOG_JAR_CLASSPATH=${LOG_JAR_CLASSPATH}:$f;
  else
    CLASSPATH=${CLASSPATH}:$f;
  fi
done

# 添加辅助 jars ,如serdes (ser和des :序列化器(Serializer)和反序列化器(Deserializer))
if [ -d "${HIVE_AUX_JARS_PATH}" ]; then
  hive_aux_jars_abspath=`cd ${HIVE_AUX_JARS_PATH} && pwd`
  for f in $hive_aux_jars_abspath/*.jar; do
    if [[ ! -f $f ]]; then
        continue;
    fi
    if $cygwin; then
	f=`cygpath -w "$f"`
    fi
    AUX_CLASSPATH=${AUX_CLASSPATH}:$f
    if [ "${AUX_PARAM}" == "" ]; then
        AUX_PARAM=file://$f
    else
        AUX_PARAM=${AUX_PARAM},file://$f;
    fi
  done
elif [ "${HIVE_AUX_JARS_PATH}" != "" ]; then 
  HIVE_AUX_JARS_PATH=`echo $HIVE_AUX_JARS_PATH | sed 's/,/:/g'`
  if $cygwin; then
      HIVE_AUX_JARS_PATH=`cygpath -p -w "$HIVE_AUX_JARS_PATH"`
      HIVE_AUX_JARS_PATH=`echo $HIVE_AUX_JARS_PATH | sed 's/;/,/g'`
  fi
  AUX_CLASSPATH=${AUX_CLASSPATH}:${HIVE_AUX_JARS_PATH}
  AUX_PARAM="file://$(echo ${HIVE_AUX_JARS_PATH} | sed 's/:/,file:\/\//g')"
fi

# 从辅助lib文件夹下添加jar包
for f in ${HIVE_HOME}/auxlib/*.jar; do
  if [[ ! -f $f ]]; then
      continue;
  fi
  if $cygwin; then
      f=`cygpath -w "$f"`
  fi
  AUX_CLASSPATH=${AUX_CLASSPATH}:$f
  if [ "${AUX_PARAM}" == "" ]; then
    AUX_PARAM=file://$f
  else
    AUX_PARAM=${AUX_PARAM},file://$f;
  fi
done
if $cygwin; then
    CLASSPATH=`cygpath -p -w "$CLASSPATH"`
    CLASSPATH=${CLASSPATH};${AUX_CLASSPATH}
else
    CLASSPATH=${CLASSPATH}:${AUX_CLASSPATH}
fi

# supress the HADOOP_HOME warnings in 1.x.x
export HADOOP_HOME_WARN_SUPPRESS=true 

# to make sure log4j2.x and jline jars are loaded ahead of the jars pulled by hadoop
export HADOOP_USER_CLASSPATH_FIRST=true

# 将类路径传递给hadoop
if [ "$HADOOP_CLASSPATH" != "" ]; then
  export HADOOP_CLASSPATH="${CLASSPATH}:${HADOOP_CLASSPATH}"
else
  export HADOOP_CLASSPATH="$CLASSPATH"
fi

# 还将hive类路径传递给hadoop
if [ "$HIVE_CLASSPATH" != "" ]; then
  export HADOOP_CLASSPATH="${HADOOP_CLASSPATH}:${HIVE_CLASSPATH}";
fi

# 检查路径中的hadoop
HADOOP_IN_PATH=`which hadoop 2>/dev/null`
if [ -f ${HADOOP_IN_PATH} ]; then
  HADOOP_DIR=`dirname "$HADOOP_IN_PATH"`/..
fi
# HADOOP_HOME env变量覆盖路径中的hadoop
HADOOP_HOME=${HADOOP_HOME:-${HADOOP_PREFIX:-$HADOOP_DIR}}
if [ "$HADOOP_HOME" == "" ]; then
  echo "Cannot find hadoop installation: \$HADOOP_HOME or \$HADOOP_PREFIX must be set or hadoop must be in the path";
  exit 4;
fi

# 将distcp添加到类路径,hive依赖于它 
#(Distcp是Hadoop自带的分布式复制程序,它是作为一个MapReduce作业来实现的,该复制作业是通过集群中并行运行的 map 来完成,此过程中没有 reduce。)
for f in ${HADOOP_HOME}/share/hadoop/tools/lib/hadoop-distcp-*.jar; do
  export HADOOP_CLASSPATH=${HADOOP_CLASSPATH}:$f;
done

HADOOP=$HADOOP_HOME/bin/hadoop
if [ ! -f ${HADOOP} ]; then
  echo "Cannot find hadoop installation: \$HADOOP_HOME or \$HADOOP_PREFIX must be set or hadoop must be in the path";
  exit 4;
fi

if [ "$SKIP_HADOOPVERSION" = false ]; then
  # 确保我们使用的是Hadoop的兼容版本
  if [ "x$HADOOP_VERSION" == "x" ]; then
      HADOOP_VERSION=$($HADOOP version 2>&2 | awk -F"\t" '/Hadoop/ {print $0}' | cut -d' ' -f 2);
  fi
  
  # 将正则表达式保存到var中,以解决引用Bash 3.1和3.2之间不兼容的问题
  hadoop_version_re="^([[:digit:]]+)\.([[:digit:]]+)(\.([[:digit:]]+))?.*$"
  
  if [[ "$HADOOP_VERSION" =~ $hadoop_version_re ]]; then
      hadoop_major_ver=${BASH_REMATCH[1]}
      hadoop_minor_ver=${BASH_REMATCH[2]}
      hadoop_patch_ver=${BASH_REMATCH[4]}
  else
      echo "Unable to determine Hadoop version information."
      echo "'hadoop version' returned:"
      echo `$HADOOP version`
      exit 5
  fi
  
  if [ "$hadoop_major_ver" -lt "1" -a  "$hadoop_minor_ver$hadoop_patch_ver" -lt "201" ]; then
      echo "Hive requires Hadoop 0.20.x (x >= 1)."
      echo "'hadoop version' returned:"
      echo `$HADOOP version`
      exit 6
  fi
fi

if [ "$SKIP_HBASECP" = false ]; then
  # HBase检测。需要bin/hbase和一个conf-dir来构建类路径条目。
  # 从HBASE_HOME和HBASE_CONF_DIR的BigTop默认值开始。
  HBASE_HOME=${HBASE_HOME:-"/usr/lib/hbase"}
  HBASE_CONF_DIR=${HBASE_CONF_DIR:-"/etc/hbase/conf"}
  if [[ ! -d $HBASE_CONF_DIR ]] ; then
    # 未显式设置,也未在BigTop位置中设置。请尝试在HBASE_HOME中查找。
    HBASE_CONF_DIR="$HBASE_HOME/conf"
  fi
  
  # 也许我们已经找到了HBase配置。如果是,请将其包含在类路径中。
  if [[ -d $HBASE_CONF_DIR ]] ; then
    export HADOOP_CLASSPATH="${HADOOP_CLASSPATH}:${HBASE_CONF_DIR}"
  fi
  
  # 查找hbase脚本。首先检查HBASE_HOME,然后询问PATH。
  if [[ -e $HBASE_HOME/bin/hbase ]] ; then
    HBASE_BIN="$HBASE_HOME/bin/hbase"
  fi
  HBASE_BIN=${HBASE_BIN:-"$(which hbase)"}
  
  # 也许我们已经找到了HBase。如果是,请将其详细信息包含在类路径中
  if [[ -n $HBASE_BIN ]] ; then
    # 不包括ZK、PB和Guava (See HIVE-2055)
    # 取决于 HBASE-8438 (hbase-0.94.14+, hbase-0.96.1+) for `hbase mapredcp` command
    for x in $($HBASE_BIN mapredcp 2>&2 | tr ':' '\n') ; do
      if [[ $x == *zookeeper* || $x == *protobuf-java* || $x == *guava* ]] ; then
        continue
      fi
      export HADOOP_CLASSPATH="${HADOOP_CLASSPATH}:${x}"
    done
  fi
fi

if [ "${AUX_PARAM}" != "" ]; then
  if [[ "$SERVICE" != beeline ]]; then
    HIVE_OPTS="$HIVE_OPTS --hiveconf hive.aux.jars.path=${AUX_PARAM}"
  fi
  AUX_JARS_CMD_LINE="-libjars ${AUX_PARAM}"
fi

SERVICE_LIST=""

#循环执行"$bin"/ext 的脚本 ,第2步我们看看这些脚本都做了什么
for i in "$bin"/ext/*.sh ; do
  . $i
done

#循环执行$bin"/ext/util 下的脚本 其实下面就一个脚本即:execHiveCmd.sh (hive命令最终也是靠它执行的)
for i in "$bin"/ext/util/*.sh ; do
  . $i
done

if [ "$DEBUG" ]; then
  if [ "$HELP" ]; then
    debug_help
    exit 0
  else
    get_debug_params "$DEBUG"
    export HADOOP_CLIENT_OPTS="$HADOOP_CLIENT_OPTS $HIVE_MAIN_CLIENT_DEBUG_OPTS"
  fi
fi

TORUN=""
for j in $SERVICE_LIST ; do
  if [ "$j" = "$SERVICE" ] ; then
    TORUN=${j}$HELP
  fi
done

# 初始化所有服务的日志记录

export HADOOP_CLIENT_OPTS="$HADOOP_CLIENT_OPTS -Dlog4j.configurationFile=hive-log4j2.properties "

if [ -f "${HIVE_CONF_DIR}/parquet-logging.properties" ]; then
  export HADOOP_CLIENT_OPTS="$HADOOP_CLIENT_OPTS -Djava.util.logging.config.file=${HIVE_CONF_DIR}/parquet-logging.properties "
else
  export HADOOP_CLIENT_OPTS="$HADOOP_CLIENT_OPTS -Djava.util.logging.config.file=$bin/../conf/parquet-logging.properties "
fi

if [[ "$SERVICE" =~ ^(hiveserver2|beeline|cli)$ ]] ; then
  # 如果进程是后台的,则不要更改终端设置
  if [[ ( ! $(ps -o stat= -p $$) =~ "+" ) && ! ( -p /dev/stdin ) && ( ! $(ps -o tty= -p $$) =~ "?" ) ]]; then
    export HADOOP_CLIENT_OPTS="$HADOOP_CLIENT_OPTS -Djline.terminal=jline.UnsupportedTerminal"
  fi
fi

# 将用于hive的log4jjar包含到类路径中
CLASSPATH="${CLASSPATH}:${LOG_JAR_CLASSPATH}"
export HADOOP_CLASSPATH="${HADOOP_CLASSPATH}:${LOG_JAR_CLASSPATH}"

if [ "$TORUN" = "" ] ; then
  echo "Service $SERVICE not found"
  echo "Available Services: $SERVICE_LIST"
  exit 7
else
  set -- "${SERVICE_ARGS[@]}"
  $TORUN "$@"
fi

2、ext 下的脚本

我们先看看ext下有哪些脚本,是不是很多,不要慌,静下心踏实的看懂才会触摸到“底层”的快乐

1、beeline.sh

# 需要参数 [host [port [db]]]
THISSERVICE=beeline
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

beeline () {
  beeline  的主入口,这篇博客先不深入讨论
  CLASS=org.apache.hive.beeline.BeeLine;

  # 仅包括 beeline 客户端jar及其依赖项
  beelineJarPath=`ls ${HIVE_LIB}/hive-beeline-*.jar`
  superCsvJarPath=`ls ${HIVE_LIB}/super-csv-*.jar`
  jlineJarPath=`ls ${HIVE_LIB}/jline-*.jar`
  hadoopClasspath=""
  if [[ -n "${HADOOP_CLASSPATH}" ]]
  then
    hadoopClasspath="${HADOOP_CLASSPATH}:"
  fi
  export HADOOP_CLASSPATH="${hadoopClasspath}${HIVE_CONF_DIR}:${beelineJarPath}:${superCsvJarPath}:${jlineJarPath}"
  export HADOOP_CLIENT_OPTS="$HADOOP_CLIENT_OPTS -Dlog4j.configurationFile=beeline-log4j2.properties "

  exec $HADOOP jar ${beelineJarPath} $CLASS $HIVE_OPTS "$@"
}

beeline_help () {
  beeline "--help"
} 

2、cleardanglingscratchdir.sh

一个清理悬挂的暂存目录的工具。在某些情况下,可能会留下一个临时目录,例如当vm重新启动时,Hive没有机会运行shutdown hook。该工具将测试一个正在使用的暂存目录,如果没有,请将其删除。

我们依靠HDFS写锁来检测是否正在使用暂存目录:

1、HDFS客户端打开HDFS文件($scratchdir/inuse.lck)进行写入,并且只在会话关闭时关闭它

2、cleardanglingscratchDir可以尝试打开$scratchdir/inuse.lck进行写入。如果相应的HiveCli/HiveServer2仍在运行,我们将得到异常。否则,我们知道会话已经结束

3、如果HiveCli/HiveServer2在没有关闭HDFS文件的情况下死亡,NameNode将在10分钟后收回租约,即死亡的HiveCli-HiveServer2所持有的HDFS文件在10分钟之后可以再次写入。一旦它变为可写入,clearhangingscratchDir将能够将其删除

THISSERVICE=cleardanglingscratchdir
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

cleardanglingscratchdir () {
  CLASS=org.apache.hadoop.hive.ql.session.ClearDanglingScratchDir
  HIVE_OPTS=''
  execHiveCmd $CLASS "$@"
}

cleardanglingscratchdir_help () {
  echo ""
  echo "usage ./hive --service cleardanglingscratchdir"
}

3、cli.sh

THISSERVICE=cli
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

# 将旧的CLI设置为默认客户端
# 如果USE_DEPRECATED_CLI未设置或不等于false,请使用旧的CLI
if [ -z "$USE_DEPRECATED_CLI" ] || [ "$USE_DEPRECATED_CLI" != "false" ]; then
  USE_DEPRECATED_CLI="true"
fi

updateCli() {
  if [ "$USE_DEPRECATED_CLI" == "true" ]; then
    export HADOOP_CLIENT_OPTS=" -Dproc_hivecli $HADOOP_CLIENT_OPTS "
    #默认hive客户端的启动类为CliDriver,java部分我们也会详细看下它
    CLASS=org.apache.hadoop.hive.cli.CliDriver
    JAR=hive-cli-*.jar
  else
    export HADOOP_CLIENT_OPTS=" -Dproc_beeline $HADOOP_CLIENT_OPTS -Dlog4j.configurationFile=beeline-log4j2.properties"
    CLASS=org.apache.hive.beeline.cli.HiveCli
    JAR=hive-beeline-*.jar
  fi
}

cli () {
  updateCli
  execHiveCmd $CLASS $JAR "$@"
}

cli_help () {
  updateCli
  execHiveCmd $CLASS $JAR "--help"
}

4、debug.sh

允许通过JDI API连接到Hive来调试它

用法:hive--debug[:逗号分隔的参数列表]

参数列表:

        recursive=<y|n> :是否也应在调试模式下启动子JVM。默认值:y

        port=<port_number> :主JVM侦听调试连接的端口。默认值:8000

        mainSuspend=<y|n>:主JVM是否应等待调试器连接的执行。默认值:y

        childSuspend=<y|n>:子JVM是否应等待调试器连接的执行。默认值:n

        swapSuspend:交换主JVM和子JVM之间的挂起选项

5、fixacidkeyindex.sh

用于检查和修复ORC文件的ACID密钥索引,(如果由于HIVE-18817而写入错误)的实用程序。将在ORC文件中检查的条件是,acid键索引中的条带数是否与ORC StripeInformation中的条带数匹配。

补充:

ORC采用混合存储结构。不是一个单纯的列式存储格式,它遵循了先水平分区,再垂直分区的理念。它支持复杂数据类型、ACID支持及内置索引支持,非常适合海量数据的存储。

ORC文件是以二进制的方式存储的,不可以直接读取。它是文件是自包含的,读取它不需考虑用户使用环境,因为它本身存储了文件数据、数据类型及编码信息,不依赖于 Hive Metastore 或任何其他外部元数据。

ORC的主体由多个Stripe(也成为条带)组成,Stripe又包含三个部分:Index Data、Row Data和Stripe Footer。索引和数据部分都按列划分,因此只需要读取所需列的数据。

THISSERVICE=fixacidkeyindex
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

fixacidkeyindex () {
  CLASS=org.apache.hadoop.hive.ql.io.orc.FixAcidKeyIndex
  HIVE_OPTS=''
  execHiveCmd $CLASS "$@"
}

使用:

./hive --service fixacidkeyindex [-h] --check-only|--recover [--backup-path <new-path>] <path_to_orc_file_or_directory>

解释:

        --check-only:检查acid orc文件的有效acid键索引,并在不修复的情况下退出

        --recover:如果需要修复,请修复acid orc文件的acid键索引

        --backup-path <new_path>:指定存储损坏文件的备份路径(默认:/tmp)

        --help (-h):Print help message

6、help.sh

打印hive命令的使用信息,帮助你使用

7、hiveburninclient.sh

HiveBurnInClient是该脚本最后执行的java主类,内容也很简单,通过jdbc连接hive,创建两张表,并加载./examples/files/kv1.txt和./examples/files/kv2.txt到表中,进行单表查询和关联查询,并记录每次执行sql的时间

THISSERVICE=hiveburninclient
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

hiveburninclient() {
  echo "Starting hiveburninclient"
  CLASS=org.apache.hive.testutils.jdbc.HiveBurnInClient
  if $cygwin; then
    HIVE_LIB=`cygpath -w "$HIVE_LIB"`
  fi
  JAR=${HIVE_LIB}/hive-service-*.jar
  exec $HADOOP jar $JAR $CLASS $HIVE_OPTS "$@"
}

hiveburninclient_help() {
  hiveburninclient -H
}

8、hiveserver2.sh

HiveServer2(HS2)是一种使客户端能够针对Hive执行查询的服务。HiveServer2是已弃用的HiveServer1的继任者。HS2支持多客户端并发和身份验证。它旨在为JDBC和ODBC等开放式API客户端提供更好的支持。

HS2是作为复合服务运行的单个进程,其中包括基于Thrift的Hive服务(TCP或HTTP)和用于web UI的Jetty web服务器。

THISSERVICE=hiveserver2
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

hiveserver2() {
  >&2 echo "$(timestamp): Starting HiveServer2"
  CLASS=org.apache.hive.service.server.HiveServer2
  if $cygwin; then
    HIVE_LIB=`cygpath -w "$HIVE_LIB"`
  fi
  JAR=${HIVE_LIB}/hive-service-[0-9].*.jar

  export HADOOP_CLIENT_OPTS=" -Dproc_hiveserver2 $HADOOP_CLIENT_OPTS "
  export HADOOP_OPTS="$HIVESERVER2_HADOOP_OPTS $HADOOP_OPTS"
  exec $HADOOP jar $JAR $CLASS $HIVE_OPTS "$@"
}

hiveserver2_help() {
  hiveserver2 -H
}

timestamp()
{
 date +"%Y-%m-%d %T"
}

9、hplsql.sh

编译并运行HPL/SQL脚本,

THISSERVICE=hplsql
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

hplsql () {
  CLASS=org.apache.hive.hplsql.Hplsql;

  # 仅包括HPL/SQL jar及其依赖项
  hplsqlJarPath=`ls ${HIVE_LIB}/hive-hplsql-*.jar`
  antlrJarPath="${HIVE_LIB}/antlr-runtime-4.5.jar"
  hadoopClasspath=""
  if [[ -n "${HADOOP_CLASSPATH}" ]]
  then
    hadoopClasspath="${HADOOP_CLASSPATH}:"
  fi
  export HADOOP_CLASSPATH="${hadoopClasspath}${HIVE_CONF_DIR}:${hplsqlJarPath}:${antlrJarPath}"

  exec $HADOOP jar ${hplsqlJarPath} $CLASS $HIVE_OPTS "$@"
}

hplsql_help () {
  hplsql "--help"
} 

10、jar.sh

用于需要Hadoop和Hive类路径和环境的应用程序,

./hive --service jar <yourjar> <yourclass> HIVE_OPTS <your_args>

11、lineage.sh

给定一个hql,LineageInfo主类负责解析给定的查询并获取沿袭信息(ParseDriver负责解析hql获取AST树)目前,这只打印给定hql的输入和输出表

THISSERVICE=lineage
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

lineage () {
  CLASS=org.apache.hadoop.hive.ql.tools.LineageInfo

  # cli特定代码
  if [ ! -f ${HIVE_LIB}/hive-exec-*.jar ]; then
    echo "Missing Hive exec Jar"
    exit 3;
  fi

  if $cygwin; then
    HIVE_LIB=`cygpath -w "$HIVE_LIB"`
  fi

  exec $HADOOP jar ${HIVE_LIB}/hive-exec-*.jar $CLASS  "$@"
}

lineage_help () {
  echo "usage ./hive --service lineage 'hql' "
} 

hive -service lineage 'select * from personal_info_test where id =2'

可以看下只输出了输入表的表名

12、llap.sh

Hive2.0时添加的这个功能

llap是Live Long and Prosper(生生不息,繁荣昌盛)的缩写,是一个常用的祝福语,意为“长寿和繁荣”,它来自于美国电视剧《星际迷航》中的一个著名台词,由主角斯波克(Spock)所说。这句话传达了对他人健康长寿和事业兴旺的祝愿。

在hive中表示Live-Long And Process(常驻进程)可以进一步提升Hive的执行速度

从脚本中我们可以看到还用python启动了在yarn上的LLAP服务

关于Hive 的 LLAP功能我们会专门用一篇博客来讲,记得关注我哟


THISSERVICE=llap
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

llap () {
  TMPDIR=$(mktemp -d /tmp/staging-yarn-XXXXXX)
  CLASS=org.apache.hadoop.hive.llap.cli.LlapServiceDriver;
  if [ ! -f ${HIVE_LIB}/hive-cli-*.jar ]; then
    echo "Missing Hive CLI Jar"
    exit 3;
  fi

  if $cygwin; then
    HIVE_LIB=`cygpath -w "$HIVE_LIB"`
  fi

  set -e;

  export HADOOP_CLIENT_OPTS=" -Dproc_llapcli $HADOOP_CLIENT_OPTS -Dlog4j.configurationFile=llap-cli-log4j2.properties "
  # hadoop 20 or newer - skip the aux_jars option. picked up from hiveconf
  $HADOOP $CLASS $HIVE_OPTS -directory $TMPDIR "$@"
  
  # check for config files
  test -f $TMPDIR/config.json

  python $HIVE_HOME/scripts/llap/yarn/package.py --input $TMPDIR "$@"

  # remove temp files
  rm -rf $TMPDIR
}

llap_help () {
  CLASS=org.apache.hadoop.hive.llap.cli.LlapServiceDriver;
  execHiveCmd $CLASS "--help"
} 

13、llapdump.sh

通过LLAP输入格式测试查询和数据检索的实用程序

THISSERVICE=llapdump
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

llapdump () {
  CLASS=org.apache.hadoop.hive.llap.LlapDump
  HIVE_OPTS=''
  execHiveCmd $CLASS "$@"
}

llapdump_help () {
  echo "usage ./hive --service llapdump [-l <url>] [-u <user>] [-p <pwd>] <query>"
  echo ""
  echo "  --location (-l)  hs2 url"
  echo "  --user (-u)      user name"
  echo "  --pwd (-p)       password"
}

hive --service llapdump 'select * from personal_info_test where id =2'

14、llapstatus.sh

负责变更LLAP服务的状态

THISSERVICE=llapstatus
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

llapstatus () {
  CLASS=org.apache.hadoop.hive.llap.cli.LlapStatusServiceDriver;
  if [ ! -f ${HIVE_LIB}/hive-cli-*.jar ]; then
    echo "Missing Hive CLI Jar"
    exit 3;
  fi

  if $cygwin; then
    HIVE_LIB=`cygpath -w "$HIVE_LIB"`
  fi

  set -e;

  export HADOOP_CLIENT_OPTS=" -Dproc_llapstatuscli $HADOOP_CLIENT_OPTS -Dlog4j.configurationFile=llap-cli-log4j2.properties "
  # hadoop 20 or newer - skip the aux_jars option. picked up from hiveconf
  $HADOOP $CLASS $HIVE_OPTS "$@"
  
}

llapstatus_help () {
  CLASS=org.apache.hadoop.hive.llap.cli.LlapStatusServiceDriver;
  execHiveCmd $CLASS "--help"
} 

15、metastore.sh

基于HadoopThriftAuthBridge 启动Metastore

HadoopThriftAuthBridge是将Thrift的SASL传输桥接到Hadoop的SASL回调处理程序和身份验证类的函数

THISSERVICE=metastore
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

metastore() {
  echo "$(timestamp): Starting Hive Metastore Server"
  CLASS=org.apache.hadoop.hive.metastore.HiveMetaStore
  if $cygwin; then
    HIVE_LIB=`cygpath -w "$HIVE_LIB"`
  fi
  JAR=${HIVE_LIB}/hive-metastore-*.jar

  # hadoop 20 or newer - skip the aux_jars option and hiveconf

  export HADOOP_CLIENT_OPTS=" -Dproc_metastore $HADOOP_CLIENT_OPTS "
  export HADOOP_OPTS="$HIVE_METASTORE_HADOOP_OPTS $HADOOP_OPTS"
  exec $HADOOP jar $JAR $CLASS "$@"
}

metastore_help() {
  metastore -h
}

timestamp()
{
 date +"%Y-%m-%d %T"
}

16、metatool.sh

此类为配置单元管理员提供了一个工具

        1、使用DataNucleus对元存储执行JDOQL (JDO其实就是jdbc,因为hive-site.xml中关于metastore的配置都是javax.jdo.xxx)

        2、执行HA名称节点升级

THISSERVICE=metatool
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

metatool () {
  HIVE_OPTS=''
  CLASS=org.apache.hadoop.hive.metastore.tools.HiveMetaTool
  execHiveCmd $CLASS "$@"
}

metatool_help () {
  HIVE_OPTS=''
  CLASS=org.apache.hadoop.hive.metastore.tools.HiveMetaTool
  execHiveCmd $CLASS "--help"
}

17、orcfiledump.sh

用于查看或修复orc文件的工具

hive --service orcfiledump --help

THISSERVICE=orcfiledump
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

orcfiledump () {
  CLASS=org.apache.orc.tools.FileDump
  HIVE_OPTS=''
  execHiveCmd $CLASS "$@"
}

orcfiledump_help () {
  echo "usage ./hive orcfiledump [-h] [-j] [-p] [-t] [-d] [-r <col_ids>] [--recover] [--skip-dump] [--backup-path <new-path>] <path_to_orc_file_or_directory>"
  echo ""
  echo "  --json (-j)                 Print metadata in JSON format"
  echo "  --pretty (-p)               Pretty print json metadata output"
  echo "  --timezone (-t)             Print writer's time zone"
  echo "  --data (-d)                 Should the data be printed"
  echo "  --rowindex (-r) <col_ids> Comma separated list of column ids for which row index should be printed"
  echo "  --recover                   Recover corrupted orc files generated by streaming"
  echo "  --skip-dump                 Used along with --recover to directly recover files without dumping"
  echo "  --backup-path <new_path>  Specify a backup path to store the corrupted files (default: /tmp)"
  echo "  --help (-h)                 Print help message"
} 

我们创建一个orc表并插入些数据来实践下这个命令

create table if not exists test.personal_info_temp_orc
(id int comment 'id',
name string comment '姓名',
age string comment '年龄' ,
sex string comment '性别:1男0女2其他' ,
telno string comment '手机号' )
row format delimited
fields terminated by ','
stored as orc
;

#hive ORC表的数据不能通过hive客户端load数据文件加载,也不能使用hdfs dfs -put上传到对应目录
#只能通过insert 方式插入数据

insert into personal_info_temp_orc select * from ods.personal_info_temp order by id ;

#获取orc文件信息

 hive --orcfiledump /user/hive/warehouse/test.db/personal_info_temp_orc/000000_0

18、rcfilecat.sh

RCFile 和 ORCFile一样都是行列式存储文件,ORCFile为优化后的行列式存储文件

THISSERVICE=rcfilecat
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

rcfilecat () {
  CLASS=org.apache.hadoop.hive.cli.RCFileCat
  HIVE_OPTS=''
  execHiveCmd $CLASS "$@"
}

rcfilecat_help () {
  echo "usage ./hive rcfilecat [--start='startoffset'] [--length='len'] "
} 

我们创建一个RCFile 格式的表并插入些数据来实践下这个命令

create table if not exists test.personal_info_temp_rc_file
(id int comment 'id',
name string comment '姓名',
age string comment '年龄' ,
sex string comment '性别:1男0女2其他' ,
telno string comment '手机号' )
row format delimited
fields terminated by ','
stored as rcfile
;

#hive ORC表的数据不能通过hive客户端load数据文件加载,也不能使用hdfs dfs -put上传到对应目录
#只能通过insert 方式插入数据

insert into personal_info_temp_rc_file select * from ods.personal_info_temp order by id ;

#获取orc文件信息

hive --rcfilecat /user/hive/warehouse/test.db/personal_info_temp_rc_file /000000_0

19、schemaTool.sh

这是一个初始化Hive元数据的工具,使用方法为(使用之前需要在hive-site.xml中正确填写元数据库的连接、用户名、密码)

schematool -dbType mysql -initSchema

THISSERVICE=schemaTool
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

schemaTool() {
  HIVE_OPTS=''
  CLASS=org.apache.hive.beeline.HiveSchemaTool
  execHiveCmd $CLASS "$@"
}

schemaTool_help () {
  HIVE_OPTS=''
  CLASS=org.apache.hive.beeline.HiveSchemaTool
  execHiveCmd $CLASS "--help"
}

20、strictmanagedmigration.sh

HiveStrictManagedMigration是这个脚本对应的java类,会循环每个数据库并把他们移动到hive-site.xml中hive.metastore.warehouse.dir对应的路径下,默认为/user/hive/warehouse

如果我们有三个数据库 default、ods、dwd

在default下建的表放在了/user/hive/warehouse 下

在ods下建的表放在了/user/hive/warehouse/ods.db 下

在dwd下建的表放在了/user/hive/warehouse/dwd.db 下

THISSERVICE=strictmanagedmigration
export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "

strictmanagedmigration () {
  CLASS=org.apache.hadoop.hive.ql.util.HiveStrictManagedMigration
  HIVE_OPTS=''
  execHiveCmd $CLASS "$@"
}

strictmanagedmigration_help () {
  strictmanagedmigration "--help"
}

21、tokentool.sh

用于操作MetaStore委派令牌的工具

22、version.sh

输出Hive的版本信息,基本每个工具都有这个功能,比如 java -version 等等

Hive 对应的命令是 hive --version 会输出版本信息、版本控制地址、提交的作者和时间、纠错码

3、ext/util下的脚本

这里只有一个脚本:execHiveCmd.sh

相比在很多脚本中都有它的身影,很多脚本对应的主类都是传递给它来运行的

CLI_JAR="hive-cli-*.jar"
BEELINE_JAR="hive-beeline-*.jar"

execHiveCmd () {
  CLASS=$1;
  shift;

  # 如果jar未作为参数传递,请使用相应的cli-jar
  if [ "$1" == "$CLI_JAR" ] || [ "$1" == "$BEELINE_JAR" ]; then
    JAR="$1"
    shift;
  else
    if [ "$USE_DEPRECATED_CLI" == "true" ]; then
      JAR="$CLI_JAR"
    else
      JAR="$BEELINE_JAR"
    fi
  fi

  # cli specific code
  if [ ! -f ${HIVE_LIB}/$JAR ]; then
    echo "Missing $JAR Jar"
    exit 3;
  fi

  if $cygwin; then
    HIVE_LIB=`cygpath -w "$HIVE_LIB"`
  fi

  # hadoop 20 or newer - skip the aux_jars option. picked up from hiveconf
  exec $HADOOP jar ${HIVE_LIB}/$JAR $CLASS $HIVE_OPTS "$@"
}

四、java部分

在cli.sh脚本中我们已经分析了默认hive客户端的启动类为CliDriver,下面我们就从CliDriver的main方法开始捋(不是主要的代码会省略掉,方便我们理清主线逻辑)

1、main

这部分代码很简单,我们接着往下看

  public static void main(String[] args) throws Exception {
    int ret = new CliDriver().run(args);
    //终止当前运行的Java虚拟机。参数用作状态代码;按照惯例,非零状态代码表示异常终止。
    System.exit(ret);
  }

2、run

  public  int run(String[] args) throws Exception {

    OptionsProcessor oproc = new OptionsProcessor();
    //从参数设置hive的环境变量,如果中间出现异常返回 1 终止虚拟机
    if (!oproc.process_stage1(args)) {
      return 1;
    }

    //......省略......
    
    //Hive客户端会话状态类
    CliSessionState ss = new CliSessionState(new HiveConf(SessionState.class));
    //把系统“标准”输入流、输出流、错误流 赋给 Hive客户端会话状态类 
    //如果期间有错误 返回 3 终止虚拟机
    ss.in = System.in;
    try {
      ss.out = new PrintStream(System.out, true, "UTF-8");
      ss.info = new PrintStream(System.err, true, "UTF-8");
      ss.err = new CachingPrintStream(System.err, true, "UTF-8");
    } catch (UnsupportedEncodingException e) {
      return 3;
    }

    //判断 hive命令后面的参数 比如 hive -e “hql” hive -f xxx.hql 等
    //这里就会根据命令行的参数初始化以下变量
    //    ss.database    执行的数据库
    //    ss.execString  -e 后面要执行的hql
    //    ss.fileName    -f 包含hql的文件
    //    ss.initFiles     
    if (!oproc.process_stage2(ss)) {
      return 2;
    }



    //设置通过命令行指定的所有属性
    HiveConf conf = ss.getConf();
    for (Map.Entry<Object, Object> item : ss.cmdProperties.entrySet()) {
      conf.set((String) item.getKey(), (String) item.getValue());
      ss.getOverriddenConfigurations().put((String) item.getKey(), (String) item.getValue());
    }

    //读取提示配置并替换变量。
    prompt = conf.getVar(HiveConf.ConfVars.CLIPROMPT);
    prompt = new VariableSubstitution(new HiveVariableSource() {
      @Override
      public Map<String, String> getHiveVariable() {
        return SessionState.get().getHiveVariables();
      }
    }).substitute(conf, prompt);
    prompt2 = spacesForString(prompt);

    if (HiveConf.getBoolVar(conf, ConfVars.HIVE_CLI_TEZ_SESSION_ASYNC)) {
      //以fire-and-forget 的方式启动会话。当需要会话的异步初始化部分时,
      //相应的getter和其他方法将根据需要等待。
      //fire-and-forget :把消息发送给服务器,但并不关心是否都到
      SessionState.beginStart(ss, console);
    } else {
      SessionState.start(ss);
    }

    ss.updateThreadName();

    //创建视图注册表
    HiveMaterializedViewsRegistry.get().init();

    //执行cli驱动程序工作
    try {
      return executeDriver(ss, conf, oproc);
    } finally {
      ss.resetThreadName();
      ss.close();
    }
  }

3、executeDriver

  /**
   * 执行Hive客户端的任务
   * @param ss CLI driver 的 session状态
   * @param conf Hive的配置信息
   * @param CLI 调用的操作处理器
   * @return 执行命令的状态
   * @throws Exception
   */  
  private int executeDriver(CliSessionState ss, HiveConf conf, OptionsProcessor oproc)
      throws Exception {

    CliDriver cli = new CliDriver();
    //设置cli的关于hive的环境变量
    cli.setHiveVariables(oproc.getHiveVariables());

    //如果指定,请使用指定的数据库
    cli.processSelectDatabase(ss);

    //执行-i init文件(始终处于静默模式)
    cli.processInitFiles(ss);

    //如果命令行带了 -e 参数 ,现在需要处理 -e 后面的 hql
    if (ss.execString != null) {
      //处理一行分号分隔的命令
      int cmdProcessStatus = cli.processLine(ss.execString);
      return cmdProcessStatus;
    }

    try {
      //如果命令行带了 -f 参数 ,现在需要处理 -f 里面的 hql
      if (ss.fileName != null) {
        return cli.processFile(ss.fileName);
      }
    } catch (FileNotFoundException e) {
      System.err.println("Could not open input file for reading. (" + e.getMessage() + ")");
      return 3;
    }
    //判断hive当前的执行引擎是否是 mr
    if ("mr".equals(HiveConf.getVar(conf, ConfVars.HIVE_EXECUTION_ENGINE))) {
      console.printInfo(HiveConf.generateMrDeprecationWarning());
    }

    //创建ConsoleReader并启动,读取用户输入,遇到“;”为一个完整的命令
    //并开启命令历史记录服务,将命令写入文件中
    setupConsoleReader();

    String line;
    int ret = 0;
    String prefix = "";
    String curDB = getFormattedDb(conf, ss);
    String curPrompt = prompt + curDB;
    String dbSpaces = spacesForString(curDB);

    //如果不是 -e -f 这样的一次性执行命令,那么就是需要一直持续解析用户输入的命令
    while ((line = reader.readLine(curPrompt + "> ")) != null) {
      if (!prefix.equals("")) {
        prefix += '\n';
      }
      //--会判断为你在hive控制台输入的注释,跳过
      if (line.trim().startsWith("--")) {
        continue;
      }
      //如果遇到 ; 就标准着hql输入结束了,开始执行 "\\;"不作为判断的标识,因为转义了
      if (line.trim().endsWith(";") && !line.trim().endsWith("\\;")) {
        line = prefix + line;
        //解析并执行这条hql 我们会在一篇博客中详细介绍一条hql执行的过程
        ret = cli.processLine(line, true);
        prefix = "";
        curDB = getFormattedDb(conf, ss);
        curPrompt = prompt + curDB;
        dbSpaces = dbSpaces.length() == curDB.length() ? dbSpaces : spacesForString(curDB);
      } else {
        prefix = prefix + line;
        curPrompt = prompt2 + dbSpaces;
        continue;
      }
    }

    return ret;
  }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值