自定义hivehook(转载)

参考:

https://blog.csdn.net/Zsigner/article/details/115306506

https://www.jianshu.com/p/30eb687e0b94

转自:https://www.modb.pro/db/390885

 

元数据管理是数据治理的基石,hive hook是一种实现元数据采集的方式,本文将介绍hive hook的优缺点,以及hive的多种hook机制,最后使用一个案例分析hook的执行过程。Hive客户端支持Hive Cli、HiveServer2等,一个完整的HQL需要经过解析、编译、优化器处理、执行器执行共四个阶段。

以Hive目前原生支持计算引擎MapReduce为例,具体处理流程如下:

1、HQL解析生成AST语法树,Antlr定义SQL的语法规则,完成SQL词法和语法解析,将SQL转化为AST;

2、语法分析得到QueryBlock遍历AST,抽象出查询的基本组成单元QueryBlock;

3、生成逻辑执行计划遍历QueryBlock,翻译为执行操作树Operator Tree;

4、逻辑优化执行器进行逻辑优化,逻辑层优化器进行Operator Tree变换,合并不必要的Reduce Sink Operator,减少shuffle数据量;

5、生成物理执行计划Task Plan,遍历Operator Tree,翻译为MapReduce任务;

6、物理优化Task Tree,构建执行计划QueryPlan,物理层优化器进行MapReduce任务的变换,生成最终的执行计划;

7、表以及其他鉴权操作;

8、执行执行引擎。

本文我们重点关注hook的分类及应用。

一、hive hook优缺点分析

(1)优点

  • 可以很方便地在各种查询阶段嵌入或者运行自定义的代码;

  • 可以被用来更新元数据;

(2)缺点

  • 使用Hooks获取到的元数据通常需要进一步解析,否则很难理解,可读性不高;

  • 会影响查询的性能。

二、hive hook的分类

Hooks (钩子)是一种事件和消息机制, 可以将事件绑定在内部 Hive 的执行流程中,而无需重新编译 Hive。Hook 提供了扩展和继承外部组件的方式。前面提到HQL的执行过程包括:HQL解析(生成AST语法树) => 语法分析(得到QueryBlock) => 生成逻辑执行计划(Operator) => 逻辑优化(LogicalOptimizer Operator) => 生成物理执行计划(TaskPlan) => 物理优化(Task Tree) => 构建执行计划(QueryPlan) => 执行引擎执行八个阶段,根据不同的 Hook 类型,可以在不同的阶段运行。关于Hooks的类型,主要分为以下几种:

(1)hive.exec.pre.hooks

生命周期:在执行引擎执行前被调用,需要对查询计划优化后才可以使用。

使用方式:修改hive-site.xml为如下配置,多个实现类使用逗号分割

<property>
<name>hive.exec.pre.hooks</name>
<value>org.apache.hadoop.hive.ql.hooks.ExecuteWithHookContext<value/>
</property>

 

(2)hive.exec.post.hooks

生命周期:在执行计划结束后,返回查询结果给客户端前被调用

使用方式:修改hive-site.xml为如下配置,多个实现类使用逗号分割

<property>
<name>hive.exec.post.hooks</name>
<value>org.apache.hadoop.hive.ql.hooks.ExecuteWithHookContext<value/>
</property>

 

(3)hive.exec.failure.hooks

生命周期:在执行计划失败后被调用

使用方式:修改hive-site.xml为如下配置,多个实现类使用逗号分割

<property>
<name>hive.exec.failure.hooks</name>
<value>org.apache.hadoop.hive.ql.hooks.ExecuteWithHookContext<value/>
</property>

 

(4)hive.metastore.init.hooks

生命周期:在HMSHandler初始化是被调用

使用方式:修改hive-site.xml为如下配置,多个实现类使用逗号分割

<property>
<name>hive.metastore.init.hooks</name>
<value>org.apache.hadoop.hive.metastore.MetaStoreInitListener<value/>
</property>

 

(5)hive.exec.driver.run.hooks

生命周期:在Driver.run开始或结束时被调用

使用方式:修改hive-site.xml为如下配置,多个实现类使用逗号分割

<property>
<name>hive.exec.driver.run.hooks</name>
<value>org.apache.hadoop.hive.ql.HiveDriverRunHook<value/>
</property>

 

(6)hive.semantic.analyzer.hook

生命周期:在对查询语句进行语义分析时被调用

使用方式:修改hive-site.xml为如下配置,多个实现类使用逗号分割

<property>
<name>hive.semantic.analyzer.hook</name>
<value>org.apache.hadoop.hive.ql.parse.AbstractSemanticAnalyzerHook<value/>
</property>

 

(7)hive.exec.query.redactor.hooks

生命周期:语法分析之后,生成执行计划之前被调用

使用方式:修改hive-site.xml为如下配置,多个实现类使用逗号分割

<property>
<name>hive.exec.query.redactor.hooks</name>
<value>org.apache.hadoop.hive.ql.hooks.Redactor<value/>
</property>

 

三、hive hook应用案例

本案例演示“执行计划执行后的hook”,相应的hive-site.xml需要修改为:

<property>
<name>hive.exec.post.hooks</name>
<value>org.apache.hadoop.hive.ql.hooks.ExecuteWithHookContext<value/>
</property>

 

示例代码:

package com.demo.hive;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.hadoop.hive.metastore.api.Database;
import org.apache.hadoop.hive.ql.QueryPlan;
import org.apache.hadoop.hive.ql.hooks.*;
import org.apache.hadoop.hive.ql.plan.HiveOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashSet;
import java.util.Set;
public class CustomPostHook implements ExecuteWithHookContext {
private static final Logger LOGGER = LoggerFactory.getLogger(CustomPostHook.class);
// 存储Hive的SQL操作类型
private static final HashSet<String> OPERATION_NAMES = new HashSet<>();
// HiveOperation是一个枚举类,封装了Hive的SQL操作类型
// 监控SQL操作类型
static {
// 建表
OPERATION_NAMES.add(HiveOperation.CREATETABLE.getOperationName());
// 修改数据库属性
OPERATION_NAMES.add(HiveOperation.ALTERDATABASE.getOperationName());
// 修改数据库属主
OPERATION_NAMES.add(HiveOperation.ALTERDATABASE_OWNER.getOperationName());
// 修改表属性,添加列
OPERATION_NAMES.add(HiveOperation.ALTERTABLE_ADDCOLS.getOperationName());
// 修改表属性,表存储路径
OPERATION_NAMES.add(HiveOperation.ALTERTABLE_LOCATION.getOperationName());
// 修改表属性
OPERATION_NAMES.add(HiveOperation.ALTERTABLE_PROPERTIES.getOperationName());
// 表重命名
OPERATION_NAMES.add(HiveOperation.ALTERTABLE_RENAME.getOperationName());
// 列重命名
OPERATION_NAMES.add(HiveOperation.ALTERTABLE_RENAMECOL.getOperationName());
// 更新列,先删除当前的列,然后加入新的列
OPERATION_NAMES.add(HiveOperation.ALTERTABLE_REPLACECOLS.getOperationName());
// 创建数据库
OPERATION_NAMES.add(HiveOperation.CREATEDATABASE.getOperationName());
// 删除数据库
OPERATION_NAMES.add(HiveOperation.DROPDATABASE.getOperationName());
// 删除表
OPERATION_NAMES.add(HiveOperation.DROPTABLE.getOperationName());
}
@Override
public void run(HookContext hookContext) throws Exception {
assert (hookContext.getHookType() == HookType.POST_EXEC_HOOK);
// 执行计划
QueryPlan plan = hookContext.getQueryPlan();
// 操作名称
String operationName = plan.getOperationName();
logWithHeader("执行的SQL语句: " + plan.getQueryString());
logWithHeader("操作名称: " + operationName);
if (OPERATION_NAMES.contains(operationName) && !plan.isExplain()) {
logWithHeader("监控SQL操作");
Set<ReadEntity> inputs = hookContext.getInputs();
Set<WriteEntity> outputs = hookContext.getOutputs();
for (Entity entity : inputs) {
logWithHeader("Hook metadata输入值: " + toJson(entity));
}
for (Entity entity : outputs) {
logWithHeader("Hook metadata输出值: " + toJson(entity));
}
} else {
logWithHeader("不在监控范围,忽略该hook!");
}
}
private static String toJson(Entity entity) throws Exception {
ObjectMapper mapper = new ObjectMapper();
// entity的类型
// 主要包括:
// DATABASE, TABLE, PARTITION, DUMMYPARTITION, DFS_DIR, LOCAL_DIR, FUNCTION
switch (entity.getType()) {
case DATABASE:
Database db = entity.getDatabase();
return mapper.writeValueAsString(db);
case TABLE:
return mapper.writeValueAsString(entity.getTable().getTTable());
}
return null;
}
/**
* 日志格式
*
* @param obj
*/
private void logWithHeader(Object obj) {
LOGGER.info("[CustomPostHook][Thread: " + Thread.currentThread().getName() + "] | " + obj);
}

}

pom.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>javademo</artifactId>
<groupId>org.example</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>CustomPostHook</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.hive</groupId>
<artifactId>hive-exec</artifactId>
<version>2.3.4</version>
</dependency>
</dependencies>
</project>

源码分析:

(1)加载jar包,在Hive的客户端中执行添加jar包的命令

hive> add jar root/CustomPostHook-1.0-SNAPSHOT.jar;

(2)修改hive-site.xml或直接在命令行修改,此处我们选择在命令行直接修改进行演示

hive> set hive.exec.post.hooks=com.demo.hive.CustomPostHook;

(3)查看表操作

hive> show tables;

查看日志:

为了更方便我们查看日志,配置一下hive的临时日志目录:

mkdir hive-log-tmp
cd hive-log-tmp/
chmod 777 .
hive --hiveconf hive.log.dir=/root/hive-log-tmp/$USER

日志详情:

INFO [873f75ee-51e6-458d-8273-44fa04b5859f main] hive.CustomPostHook: [CustomPostHook][Thread: 873f75ee-51e6-458d-8273-44fa04b5859f main] | 执行的SQL语句: show tables
INFO [873f75ee-51e6-458d-8273-44fa04b5859f main] hive.CustomPostHook: [CustomPostHook][Thread: 873f75ee-51e6-458d-8273-44fa04b5859f main] | 操作名称: SHOWTABLES
INFO [873f75ee-51e6-458d-8273-44fa04b5859f main] hive.CustomPostHook: [CustomPostHook][Thread: 873f75ee-51e6-458d-8273-44fa04b5859f main] | 不在监控范围,忽略该hook!

(4)建表操作

CREATE TABLE testposthook(
id int COMMENT "id",
name string COMMENT "姓名"
)COMMENT "建表_测试Hive Hooks"
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
LOCATION '/user/hive/warehouse/';

(5)查看建表操作的日志

INFO [873f75ee-51e6-458d-8273-44fa04b5859f main] hive.CustomPostHook: [CustomPostHook][Thread: 873f75ee-51e6-458d-8273-44fa04b5859f main] | 操作名称: CREATETABLE


INFO [873f75ee-51e6-458d-8273-44fa04b5859f main] hive.CustomPostHook: [CustomPostHook][Thread: 873f75ee-51e6-458d-8273-44fa04b5859f main] | 监控SQL操作


INFO [873f75ee-51e6-458d-8273-44fa04b5859f main] hive.CustomPostHook: [CustomPostHook][Thread: 873f75ee-51e6-458d-8273-44fa04b5859f main] | Hook metadata输入值: null


INFO [873f75ee-51e6-458d-8273-44fa04b5859f main] hive.CustomPostHook: [CustomPostHook][Thread: 873f75ee-51e6-458d-8273-44fa04b5859f main] | Hook metadata输出值: {"name":"default","description":"Default Hive database","locationUri":"hdfs://localhost:8020/user/hive/warehouse","parameters":{},"privileges":null,"ownerName":"public","ownerType":"ROLE","createTime":1638515629,"setOwnerName":true,"setOwnerType":true,"setPrivileges":false,"setName":true,"setDescription":true,"setLocationUri":true,"setParameters":true,"setCreateTime":true,"parametersSize":0}
INFO [873f75ee-51e6-458d-8273-44fa04b5859f main] hive.CustomPostHook: [CustomPostHook][Thread: 873f75ee-51e6-458d-8273-44fa04b5859f main] | Hook metadata输出值: {"tableName":"testposthook","dbName":"default","owner":"root","createTime":1641284654,"lastAccessTime":0,"retention":0,"sd":{"cols":[],"location":null,"inputFormat":"org.apache.hadoop.mapred.SequenceFileInputFormat","outputFormat":"org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat","compressed":false,"numBuckets":-1,"serdeInfo":{"name":null,"serializationLib":"org.apache.hadoop.hive.serde2.MetadataTypedColumnsetSerDe","parameters":{"serialization.format":"1"},"setName":false,"setParameters":true,"parametersSize":1,"setSerializationLib":true},"bucketCols":[],"sortCols":[],"parameters":{},"skewedInfo":{"skewedColNames":[],"skewedColValues":[],"skewedColValueLocationMaps":{},"setSkewedColNames":true,"setSkewedColValues":true,"setSkewedColValueLocationMaps":true,"skewedColNamesSize":0,"skewedColNamesIterator":[],"skewedColValuesSize":0,"skewedColValuesIterator":[],"skewedColValueLocationMapsSize":0},"storedAsSubDirectories":false,"setLocation":false,"setOutputFormat":true,"setSerdeInfo":true,"setBucketCols":true,"setSortCols":true,"setSkewedInfo":true,"colsIterator":[],"setCompressed":false,"setNumBuckets":true,"bucketColsSize":0,"bucketColsIterator":[],"sortColsSize":0,"sortColsIterator":[],"setStoredAsSubDirectories":false,"setParameters":true,"parametersSize":0,"colsSize":0,"setInputFormat":true,"setCols":true},"partitionKeys":[],"parameters":{},"viewOriginalText":null,"viewExpandedText":null,"tableType":"MANAGED_TABLE","privileges":null,"temporary":false,"ownerType":"USER","setOwnerType":true,"setTableName":true,"setPrivileges":false,"setOwner":true,"setPartitionKeys":true,"setViewOriginalText":false,"setViewExpandedText":false,"setTableType":true,"setRetention":false,"partitionKeysIterator":[],"setTemporary":false,"setDbName":true,"setSd":true,"setParameters":true,"setCreateTime":true,"setLastAccessTime":false,"parametersSize":0,"partitionKeysSize":0}

以上,本文对六种hive hook的配置方式进行了讲解,最后使用一个案例演示了“hive.exec.post.hooks”的执行效果,这就是使用hive hook采集技术元数据的一种方式,需要说明的是,开启hive hook会对作业的执行性能有一定的影响,在生产环境是否开启hive hook,需要根据实际使用场景灵活配置。
PS:文章参考了一部分网上的文章,感谢! 

posted @ 2022-06-17 10:43  Mars.wang  阅读(985)  评论(0编辑  收藏  举报