一、源码下载
下面是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,只会解析第一条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;
}