Apache Hive初读(一)

1. 背景

hive是基于hadoop的一个数据仓库工具,用来进行数据提取、转化、加载,这是一种可以存储、查询和分析存储在Hadoop中的大规模数据的机制。hive数据仓库工具能将结构化的数据文件映射为一张数据库表,并提供sql查询功能,能将sql转变成MapReduce任务来执行。Hive的优点是学习成本低,可以通过类似SQL语句实现快速MapReduce统计,使MapReduce变得更加简单,而不必开发专门的MapReduce应用程序。hive是十分适合数据仓库的统计分析和Windows注册表文件。

2. 目标

本文的目标,从一个简单的语法DESC TABLE来梳理Hive的代码,介绍hive针对这个语法的编译和运行原理。从而让大家对hive有个整体的了解。

3. Apache Hive代码初读 

3.1 入口函数

/hive/cli/src/java/org/apache/hadoop/hive/cli/CliDriver.java


public static void main(String[] args)throws Exception {

int ret =new CliDriver().run(args);

System.exit(ret);

}

main函数是入口,新建了一个CliDriver对象,并且传入参数执行了run函数。

之后的执行路径如下,不在赘述:

a. /hive/cli/src/java/org/apache/hadoop/hive/cli/CliDriver.java:run()

b. /hive/cli/src/java/org/apache/hadoop/hive/cli/CliDriver.java:executeDriver()

c. /hive/cli/src/java/org/apache/hadoop/hive/cli/CliDriver.java:processLine()

d. /hive/cli/src/java/org/apache/hadoop/hive/cli/CliDriver.java:processCmd

e. /hive/cli/src/java/org/apache/hadoop/hive/cli/CliDriver.java: processLocalCmd

f./hive/ql/src/java/org/apache/hadoop/hive/ql/Driver.java: run()

g. /hive/ql/src/java/org/apache/hadoop/hive/ql/Driver.java:runInternal()

h. /hive/ql/src/java/org/apache/hadoop/hive/ql/Driver.java:compileInternal()

i. /hive/ql/src/java/org/apache/hadoop/hive/ql/Driver.java:compile()

3.2 编译过程

a. 生成抽象语法树:这一步主要是把describe table这样的sql语句转成一个语法书,过程比较复杂,涉及到编译原理的知识,不在此展开。大致是检查语法是否错误,识别sql中包含的操作实体,以及对每个实体的操作。

ParseDriver pd =new ParseDriver();

ASTNode tree = pd.parse(command,ctx);

b. 语义分析

sem.analyze(tree,ctx);

由于desc是DDL操作,实际进行语义分析的类是:/hive/ql/src/java/org/apache/hadoop/hive/ql/parse/DDLSemanticAnalyzer.java:analyzeDescribeTable(),详细分析下这个函数:


private void analyzeDescribeTable(ASTNode ast)throws SemanticException {

ASTNode tableTypeExpr = (ASTNode) ast.getChild(0);

//找出表的全路径名如:sytem.availability

String qualifiedName =

QualifiedNameUtil.getFullyQualifiedName((ASTNode) tableTypeExpr.getChild(0));

//从metastore里面验证该表是否存在

String tableName =

QualifiedNameUtil.getTableName(db, (ASTNode)(tableTypeExpr.getChild(0)));

//从metastore里面验证数据库是否存在

String dbName =

QualifiedNameUtil.getDBName(db, (ASTNode)(tableTypeExpr.getChild(0)));

//从metastore里面验证分区是否存在

Map partSpec =

QualifiedNameUtil.getPartitionSpec(db, tableTypeExpr, tableName);

//describe操作可以精确到列,查看列的创建情况

String colPath = QualifiedNameUtil.getColPath(

db, tableTypeExpr, (ASTNode) tableTypeExpr.getChild(0), qualifiedName, partSpec);

// if database is not the one currently using

// validate database

  if (dbName !=null) {

validateDatabase(dbName);

}

if (partSpec !=null) {

validateTable(tableName, partSpec);

}

//创建一个DesctableDesc对象,该对象指示执行describe任务时,要做的事。支持参数:formatted, extended, pretty等3中描述方式

DescTableDesc descTblDesc =new DescTableDesc(

ctx.getResFile(), tableName, partSpec, colPath);

boolean showColStats =false;

if (ast.getChildCount() ==2) {

int descOptions = ast.getChild(1).getType();

descTblDesc.setFormatted(descOptions == HiveParser.KW_FORMATTED);

descTblDesc.setExt(descOptions == HiveParser.KW_EXTENDED);

descTblDesc.setPretty(descOptions == HiveParser.KW_PRETTY);

// in case of "DESCRIBE FORMATTED tablename column_name" statement, colPath

// will contain tablename.column_name. If column_name is not specified

// colPath will be equal to tableName. This is how we can differentiate

// if we are describing a table or column

    if (!colPath.equalsIgnoreCase(tableName) && descTblDesc.isFormatted()) {

showColStats =true;

}

}

inputs.add(new ReadEntity(getTable(tableName)));

//创建task,这里创建DDLTask

Task ddlTask = TaskFactory.get(new DDLWork(getInputs(), getOutputs(),

descTblDesc),conf);

rootTasks.add(ddlTask);

String schema = DescTableDesc.getSchema(showColStats);

setFetchTask(createFetchTask(schema));

LOG.info("analyzeDescribeTable done");

}


语义分析的过程是:根据ASTNode区分要执行的操作,如DDL,EXPLAIN, DML等,分别对待这些操作,DDL这类操作不需要执行mapreduce任务,直接从metastore里面取出数据返回。最后生成对应的Task

3.2 任务执行过程

生成DDLTask之后就会去运行任务,执行DDLTask里的execute()函数:/hive/ql/src/java/org/apache/hadoop/hive/ql/exec/DDLTask.java:execute(),接着根据describe talbe操作调用:describeTable()函数。


private int describeTable(Hive db, DescTableDesc descTbl)throws HiveException {

String colPath = descTbl.getColumnPath();

String tableName = descTbl.getTableName();

// describe the table - populate the output stream

  Table tbl = db.getTable(tableName,false);

Partition part =null;

DataOutputStream outStream =null;

try {

Path resFile =new Path(descTbl.getResFile());

if (tbl ==null) {

FileSystem fs = resFile.getFileSystem(conf);

outStream = fs.create(resFile);

outStream.close();

outStream =null;

throw new HiveException(ErrorMsg.INVALID_TABLE, tableName);

}

if (descTbl.getPartSpec() !=null) {

part = db.getPartition(tbl, descTbl.getPartSpec(),false);

if (part ==null) {

FileSystem fs = resFile.getFileSystem(conf);

outStream = fs.create(resFile);

outStream.close();

outStream =null;

throw new HiveException(ErrorMsg.INVALID_PARTITION,

StringUtils.join(descTbl.getPartSpec().keySet(),','), tableName);

}

tbl = part.getTable();

}

}catch (IOException e) {

throw new HiveException(e, ErrorMsg.GENERIC_ERROR, tableName);

}finally {

IOUtils.closeStream(outStream);

}

try {

LOG.info("DDLTask: got data for " + tbl.getTableName());

Path resFile =new Path(descTbl.getResFile());

FileSystem fs = resFile.getFileSystem(conf);

outStream = fs.create(resFile);

List cols =null;

List colStats =null;

if (colPath.equals(tableName)) {

cols = (part ==null || tbl.getTableType() == TableType.VIRTUAL_VIEW) ?

tbl.getCols() : part.getCols();

if (!descTbl.isFormatted()) {

cols.addAll(tbl.getPartCols());

}

}else {

Deserializer deserializer = tbl.getDeserializer(true);

if (deserializerinstanceof AbstractSerDe) {

String errorMsgs = ((AbstractSerDe) deserializer).getConfigurationErrors();

if (errorMsgs !=null && !errorMsgs.isEmpty()) {

throw new SQLException(errorMsgs);

}

}

cols = Hive.getFieldsFromDeserializer(colPath, deserializer);

if (descTbl.isFormatted()) {

// when column name is specified in describe table DDL, colPath will

// will be table_name.column_name

        String colName = colPath.split("\\.")[1];

String[] dbTab = Utilities.getDbTableName(tableName);

List colNames =new ArrayList();

colNames.add(colName.toLowerCase());

if (null == part) {

colStats = db.getTableColumnStatistics(dbTab[0].toLowerCase(), dbTab[1].toLowerCase(), colNames);

}else {

List partitions =new ArrayList();

partitions.add(part.getName());

colStats = db.getPartitionColumnStatistics(dbTab[0].toLowerCase(), dbTab[1].toLowerCase(), partitions, colNames).get(part.getName());

}

}

}

fixDecimalColumnTypeName(cols);

// In case the query is served by HiveServer2, don't pad it with spaces,

// as HiveServer2 output is consumed by JDBC/ODBC clients.

    boolean isOutputPadded = !SessionState.get().isHiveServerQuery();

formatter.describeTable(outStream, colPath, tableName, tbl, part,

cols, descTbl.isFormatted(), descTbl.isExt(),

descTbl.isPretty(), isOutputPadded, colStats);

LOG.info("DDLTask: written data for " + tbl.getTableName());

outStream.close();

outStream =null;

}catch (SQLException e) {

throw new HiveException(e, ErrorMsg.GENERIC_ERROR, tableName);

}catch (IOException e) {

throw new HiveException(e, ErrorMsg.GENERIC_ERROR, tableName);

}finally {

IOUtils.closeStream(outStream);

}

return 0;

}


这段代码主要是根据tableName从metastore里面读出创建表的元数据,按照schema格式输出。细节大家可以再细读。

4.总结

本文主要介绍了describe table这个操作在Hive中的编译运行过程。可以看出来decribe table操作比较简单,从metastore里面取出表的元数据输出。通过这个操作的代码追踪我们也可以得出一些结论:

a. hive根据不同的操作,区分了不同的类型,如DDL, EXPLAIN, DML操作等,分别有不同的编译和运行方法

b. 对于DDL操作,无需执行mapreduce任务,直接从metastore里面读取数据

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342