【hive和spark】hive和spark数据lineage血缘实现思路

一、背景

hive、spark、flink是hadoop最常用的,三个计算入口。hive最古老,同时有metastore,spark算的快,flink流技术支持最好。目前hive和spark融合度高,flink较为独行。

1.1 spark、hive关系:

hive和spark相互依存,如hive使用spark引擎,进行计算(当然也能使用tez引擎),spark连接hive metastore,获取表的元数据关系。本文不讨论tez引擎的问题。仅讨论hive使用spark引擎,和spark连接spark元数据情形。

hive使用spark引擎:
虽然hive使用spark引擎,但是不会触发spark的钩子函数的。仅作为引擎使用。

spark连接hive metastore。
spark内执行ddl的时候,spark监听ddl可以捕捉到。同时由于修改hive metastore,hive metastore的钩子也能监听到。所以spark 和 hive都能监听到,如果都监听务必仅向数据库写入一份,或者直接都用merge方式写入。

1.2 目前已经有框架

1.2.1、datahub

datahub
领英,创建,完全开源。社区较为活跃,依赖图数据库,文档详尽,支持很多数据源。定义为数据发现平台(Data Discovery Platform ),数据管理平台,集成了元数据管理和数据血缘功能。拥有UI界面。由python和java编写完成,元数据导入等使用python脚本完成。

数据血缘的表示如下:
source
在这里插入图片描述

在这里插入图片描述

1.2.2、atlas

atlas
阿特拉斯,由apache主导,社区活跃略低,较重,依赖hbase。集成数据发现功能,数据定义提较为灵活。
支持

在这里插入图片描述

1.3、血缘粒度

hive在不太老的版本上可以实现column级血缘,下文有介绍。
spark目前较难实现column级血缘。只能实现table级。

1.4、项目架构

其中有些是比较特别的,比如SQL形式的create table ascreate view,和SPARK的save into datasource,都是又是ddl也是dml的,会被执行两次。但由于项目中数据模型的一致性且存储模型均使用merge方式,所以最终存储到图数据的数据不会受到影响。
在这里插入图片描述

二、hive数据血缘实现思路

2.1 hive数据血缘捕捉实现

实现org.apache.hadoop.hive.ql.hooks.ExecuteWithHookContext
可以获取到任意类型的sql,比如insert,create table as,explain,use database等等。
需要注意的是:
(1)此类监测的hiveserver2,因此spark、flink的ddl等都不会被此类监测到。
(2)此类能检测到sql文本内容。

此类只有一个方法

void run(HookContext var1) throws Exception;

从HookContext中可以获取到

// 查询计划
    val plan: QueryPlan = hookContext.getQueryPlan
    // 查询的operation,即查询类型
    // 例如 org.apache.hadoop.hive.ql.plan.HiveOperation#QUERY 通过 getOperationName方法可以和op进行匹配
    val op = plan.getOperationName
    // 通过SessionState可以获取到当前的SessionId和当前数据库名,还有当前Session内的所有临时temporary表名称,还有当前的HiveConf。
    val ss: SessionState = SessionState.get
    // 获取当前Session的用户名称
    val userName = hookContext.getUgi.getUserName
// 执行时间
    val queryTime = getQueryTime(plan)
    // 执行的Sql语句
    val sql = plan.getQueryStr.trim
    // 通过HiveMetaStoreClient 可以实时再查询库表的元数据。
    // 使用ms需要注意:如果此表已经drop或者alter就不要使用ms查询了,否则程序报异常。
    val ms: HiveMetaStoreClient = getMetastore(ss.getConf)

和血缘有关系的操作有一些内容:
本例中view相关的也纳入血缘范畴。

op match {
   
      case dml if (Set(HiveOperation.QUERY.getOperationName  // insert into 
        , HiveOperation.CREATETABLE_AS_SELECT.getOperationName// create table as 
        , HiveOperation.CREATEVIEW.getOperationName // create view
        , HiveOperation.ALTERVIEW_RENAME.getOperationName // alter view as
        ).contains(op)) => {
   
      // 下文将详细讲解
      }
      case load if Set(HiveOperation.LOAD.getOperationName).contains(load) => {
   
        LOG.info(s"lineage event: ${
     op}!")
      // 封装对象 
      }
      case truncate if Set(HiveOperation.TRUNCATETABLE.getOperationName).contains(truncate) => {
   
      // 封装对象
      }
      case other=>  LOG.info(s"lineage event: ${
     op} passed!")
}

这里着重说明case dml,情形较为复杂:
主要分为finalSelOps是否为空两种情况。

  def toScalaLinkedHashMap[K, V](input: java.util.Map[K, V]): LinkedHashMap[K, V] = {
   
    val output: LinkedHashMap[K, V] = LinkedHashMap.empty
    output.putAll(input)
    output
  }
    // index是指每一列都对应已改索引,血缘信息需要通过index查找到
    val index: LineageCtx.Index = hookContext.getIndex
    // finalSelOps就是select最终获取的列的信息
    // LinkedHashMap[列名称,[SelectOperator,sink表]]
    val finalSelOps: mutable.LinkedHashMap[String, ObjectPair[SelectOperator, Table]] = toScalaLinkedHashMap(index.getFinalSelectOps)

// hive2.3.9以后修复了finalSelOps为空的bug,能获取列的血缘信息。
// 具体参考:https://issues.apache.org/jira/browse/HIVE-14706
if (finalSelOps.values.isEmpty) {
   
  // 无法获取最终选取的列的信息,就只能获取表的血缘了。
  // plan.getInputs获取数据的source,可能是表,视图,临时表,也可能是 insert into values(我是临时表) 生成
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值