其实根据hivehook的插入阶段来看,我们是可以拿到hive的执行计划的。
本次就从这个执行计划下手,在hook中实现血缘的解析。
- Pre-semantic-analyzer hooks:在Hive在查询字符串上运行语义分析器之前调用。
- Post-semantic-analyzer hooks:在Hive在查询字符串上运行语义分析器之后调用。
- Pre-driver-run hooks:在driver执行查询之前调用。
- Post-driver-run hooks:在driver执行查询之后调用。
- Pre-execution hooks:在执行引擎执行查询之前调用。
- Post-execution hooks:在查询执行完成之后以及将结果返回给用户之前调用。
- Failure-execution hooks:当查询执行失败时调用。
public class LineageHook implements ExecuteWithHookContext {
private static final HashSet<String> OPERATION_NAMES = new HashSet<String>();
private static final HashSet<String> INPUTS = new HashSet<String>();
private static final HashSet<String> OUTPUTS = new HashSet<String>();
static {
OPERATION_NAMES.add(HiveOperation.QUERY.getOperationName());
OPERATION_NAMES.add(HiveOperation.CREATETABLE_AS_SELECT.getOperationName());
OPERATION_NAMES.add(HiveOperation.ALTERVIEW_AS.getOperationName());
OPERATION_NAMES.add(HiveOperation.CREATEVIEW.getOperationName());
OPERATION_NAMES.add(HiveOperation.LOAD.getOperationName());//在原有基础上,开放load语句
}
@Override
public void run(HookContext hookContext) throws Exception {
INPUTS.clear();
OUTPUTS.clear();
QueryPlan plan = hookContext.getQueryPlan();
LineageCtx.Index index = hookContext.getIndex();
SessionState ss = SessionState.get();
if (ss != null && index != null
&& OPERATION_NAMES.contains(plan.getOperationName())
&& !plan.isExplain()) {
System.out.println(plan.getOperationName());
//输出
for (WriteEntity output : plan.getOutputs()) {
Entity.Type entityType = output.getType();
if (entityType == Entity.Type.TABLE
|| entityType == Entity.Type.PARTITION
|| entityType == Entity.Type.LOCAL_DIR //放行LOCAL_DIR
|| entityType == Entity.Type.DFS_DIR //放行DFS_DIR
) {
Table trgTb = output.getTable();
String trgTbName = null;
if (trgTb!=null) {
trgTbName = trgTb.getDbName()+"."+trgTb.getTableName();
}else {
trgTbName = output.getD().toString();
//hdfs://master:8020/tmp/hive/admin/27808155-878a-4446-9c4e-a2f3388301fc/hive_2020-06-19_16-47-52_939_789950828629061887-1/-mr-10001
if (trgTbName.matches("hdfs://.+/tmp/hive/.+")) {// 过滤MR中间临时落地数据的路径
continue;
}
}
// System.out.println("target table "+trgTbName);l
if (OUTPUTS.contains(trgTbName)) {
continue;
}else {
OUTPUTS.add(trgTbName);
}
break;
}
}
if (OUTPUTS.size()==0) {//如果没有输出,不获取输入,相当于屏蔽了无输出的简单Query
return;
}
//输入
for (ReadEntity input : plan.getInputs()) {
Entity.Type entityType = input.getType();
if (entityType == Entity.Type.TABLE
|| entityType == Entity.Type.PARTITION
|| entityType == Entity.Type.LOCAL_DIR
|| entityType == Entity.Type.DFS_DIR
) {
Table srcTb = input.getTable();
String srcTbName = null;
if (srcTb!=null) {
srcTbName = srcTb.getDbName()+"."+srcTb.getTableName();
}else {
srcTbName = input.getD().toString();
if (srcTbName.matches("hdfs://.+/tmp/hive/.+")) {
continue;
}
}
INPUTS.add(srcTbName); //用HashSet装输入源名称,因为多分区输入时会有多个ReadEntity 这些Entity表名是相同的
// System.out.println("src table "+srcTbName);
}
}
System.out.println("INPUT="+String.join(",",INPUTS));
System.out.println("OUTPUT="+String.join(",",OUTPUTS));
}
}
}
项目依赖跟上一篇hivehook一样。
一次性执行在hive命令行可以执行下面两条命令。但会话结束就结束了。
add jar /opt/module/hive-hook-1.0-SNAPSHOT.jar
set hive.exec.post.hooks=cc.eslink.hook.LineageHook;
永久生效的话,上传jar包,添加jar包到hive-lib,修改hive-site.xml
/opt/cloudera/parcels/CDH/lib/hive/lib
这样就进一步拿到了血缘关系。