基于DataHub元数据血缘管理实施方案

文章详细介绍了元数据管理的实施方案,包括元数据分类(技术元数据和业务元数据),元数据标签体系的构建,表元数据的抽取,以及血缘元数据的构建方法,如基于push和REST-API机制。此外,还涉及到了使用Datahub进行元数据操作,如CLI手工删除元数据,以及基于Graphiql查询血缘和上下游数量。
摘要由CSDN通过智能技术生成
​

L

目录

1. 元数据管理实施方案总览

2. 元数据分类



3. 元数据标签体系





4. 表元数据








5. 血缘元数据










6. cli手工删除元数据







7. 基于graphiql查询血缘以及上下游数量



1. 元数据管理实施方案总览


2. 元数据分类
元数据按用途的不同分为两类:技术元数据( Technical Metadata)和业务元数据( Business Metadata )

2.1 技术元数据
技术元数据是存储关于数据仓库系统技术细节的数据,用于开发和管理数据仓库使用的数据。常见的技术元数据有:

分布式计算系统存储元数据:如Hive表、列、分区等信息。记录了表的表名。分区信息、责任人信息、文件大小、表类型,生命周期,以及列的字段名、字段类型、字段备注、是否是分区 段等信息。
分布式计算系统运行元数据:如 Spark上所有作业运行等信息:类似于 Job 日志,包括作业类型、实例名称、输入输出、 SQL 、执行时间。数据开发平台中数据同步、计算任务、任务调度等信息。
数据质量和运维相关元数据:如任务监控、运维报警、数据质量、故障等信息,包括任务监控运行日志、告警配置及运行日志、故障信息等。
2.2 业务元数据
业务元数据从业务角度描述了数据仓库中的数据,它提供了介于使用者和实际系统之间的语义层,使得不懂计算机技术的业务人员也能够看懂数据仓库中的数据。常见的业务元数据有:

Data 元数据:如维度及属性、业务过程、指标等的规范化定义,用于更好地管理和使用数据。
数据应用元数据:如数据报表、数据产品等的配置和运行元数据。
3. 元数据标签体系
    利用元数据标签不仅可以节约研发人员的时间成本,同时对公司内部的非研发人员来说,也可以更直观地理解数据、利用数据,从而提升数据的研发效率。所以在datahub里可以预先建设domain共和glossary词库用于对数据进行打标。

 基础标签 
数据的存储情况
访问情况
数据安全等级
数仓标签
数据是增量/全量
是否可再生
数据的生命周期
 业务标签
数据归属的主题域
产品线BU
业务类型
潜在标签
这类标签主要是为了说明数据潜在的应用场景

社交
媒体
广告
电商
金融
4. 表元数据
4.1  基于pull机制抽取元数据
    Datahub的pull是基于插件的方式。可以检查数据源获取插件Source,转换插件transformer,获取插件Sink。插件安装命令如:pip install 'acryl-datahub[mysql]'

使用命令查看当前已安装的插件python3 -m datahub check plugins



web端ui方式


cli端yml方式


yml解析
模板

source:
type: mysql #数据源可以是hive等其他,相应的config配置有细微不同
config:
   host_port: 172.16.8.69:3308
   database: test
    username: "root"
    password: "root"
    profiling: #hive中统计慎用,容易资源拉满,集群垮掉
        enabled: True
        include_field_min_value: True
       include_field_max_value: True 
    stateful_ingestion: #开启状态,在拉取全数据时候会自动增删
      enabled: True
      remove_stale_metadata: True
#大部分情况transformer不需要配置
transformers:
- type: "simple_remove_dataset_ownership"
config:
owner_urns:
     - "urn:li:corpuser:username1"
     - "urn:li:corpuser:username2"
     - "urn:li:corpGroup:groupname"
ownership_type: "PRODUCER"
#默认就是datahub-rest
sink:
   type: "datahub-rest"
config:
    server: 'http://localhost:8080'
pipeline_name: mysql_pipline #要支持状态,必须有pipline名称
datahub_api: # Optional. But if provided, this config will be used by the "datahub" ingestion state provider.
server: "http://localhost:8080"

source

更多config 详情见官方文档Input objects | DataHub (datahubproject.io)

source:
type: mysql #数据源可以是hive等其他,相应的config配置有细微不同
config:
   host_port: 172.16.8.69:3308
   database: test
    username: "root"
    password: "root"
    profiling:
        enabled: True
        include_field_min_value: True
       include_field_max_value: True 
    stateful_ingestion: #开启状态,在拉取全数据时候会自动增删
      enabled: True
      remove_stale_metadata: True

transformer(可不配置)

(1)添加标签

使用 simple_add_dataset_tags 模块给添加标签。
可以使用自己的模块函数通过 add_dataset_tags 自定义标签。
transformers:
- type: "simple_add_dataset_tags"
config:
tag_urns:
- "urn:li:tag:NeedsDocumentation"
- "urn:li:tag:Legacy"

(2)更改拥有者

使用 simple_remove_dataset_ownership 清除数据拥有者。
transformers:
- type: "simple_remove_dataset_ownership"
config: {}

使用 simple_add_dataset_ownership 添加一系列用户。
transformers:
- type: "simple_add_dataset_ownership"
config:
owner_urns:
- "urn:li:corpuser:username1"
- "urn:li:corpuser:username2"
- "urn:li:corpGroup:groupname"
ownership_type: "PRODUCER"

(3)根据数据集 urn 模式设置拥有者关系,为不同的数据集设置不同的拥有者。

transformers:
- type: "pattern_add_dataset_ownership"
config:
owner_pattern:
rules:
".*example1.*": ["urn:li:corpuser:username1"]
".*example2.*": ["urn:li:corpuser:username2"]
ownership_type: "DEVELOPER"

(4)标记数据集状态

不想在界面看到某个数据集,就需要将其标记为“已移除”。
transformers:
- type: "mark_dataset_status"
config:
removed: true

(5)添加数据集浏览路径

通过转换为数据集添加浏览路径。有 3 个可选变量:

ENV:传递的环境变量,默认 prod。

PLATFORM:DataHub 支持的平台,例如:mysql、postgres。

DATASET_PARTS:斜线分割的数据集名称,例如:database_name/[table_name]。

这样就会给 hive 数据库的 cn_test_dm_book.biz_batch_operate_record浏览路径 表生成一个:

/prod/hive/cn_test_dm_book/biz_batch_operate_record浏览路径。

transformers:
- type: "set_dataset_browse_path"
config:
path_templates:
- /ENV/PLATFORM/DATASET_PARTS
#不需要 ENV 并且固定路径中的某部分。
transformers:
- type: "set_dataset_browse_path"
config:
path_templates:
- /PLATFORM/marketing_db/DATASET_PARTS
这会为 MySQL 数据库 sales.orders 表产生浏览路径:/mysql/marketing_db/sales/orders。
可以设置多浏览路径。不同的人对同样的数据资产有不同的名字。
transformers:
- type: "set_dataset_browse_path"
config:
path_templates:
- /PLATFORM/marketing_db/DATASET_PARTS
- /data_warehouse/DATASET_PARTS
这样会生成 2 个浏览路径:
① /mysql/marketing_db/sales/orders
② /data_warehouse/sales/orders
 

sink


将元数据事件输出到标准输出。
用于试验和调试。
source:
source configs
sink:
type: "console"



① DataHub Rest
使用 GMS Rest 接口将元数据推送到 DataHub。
任何错误可以立即被报告。
还有一些字段可以设置:timeout_sec、token、extra_headers、max_threads。
source:
source configs
sink:
type: "datahub-rest"
config:
   server: "http://datahubip:8080"
② DataHub Kafka
通过发布消息到 Kafka 将元数据推送至 DataHub。
异步的可以处理更高的流量。
有一些跟连接相关的字段配置信息。

source:
source configs
sink:
type: "datahub-kafka"
config:
   connection:
   bootstrap: "localhost:9092"
   schema_registry_url: "http://datahubip:8081"



将元数据输出到文件。
使用 File 汇可以将源数据源的处理和推送从 DataHub 解耦。
也适合于调试目的。
使用 File 源可以从 File 汇的数据文件读取元数据。
source:
source configs
sink:
type: file
config:
   filename: ./path/to/mce/file.json
 

yml模板
不论是ui方式还是cli方式都需要配置yml文件。例举如下我司常用的组件,更多模板详见Athena | DataHub (datahubproject.io)

mysql模板

能力

地位

笔记

数据探查   ✅  (可选)通过配置启用
检测已删除的实体   ✅  通过有状态引入启用
域  ✅  通过配置字段支持domain
平台实例   ✅  默认启用
此插件提取以下内容:数据库、架构和表的元数据 通过可选的 SQL 分析与每个表关联的列类型和架构 表、行和列统计信息

正在上传…重新上传取消

clickhouse模板

能力

地位

笔记

数据探查   ✅  (可选)通过配置启用
检测已删除的实体   ✅  通过有状态引入启用
此插件提取以下内容:

表、视图、实例化视图和字典的元数据
与每个表关联的列类型(*聚合函数和日期时间与时区除外)
通过可选的 SQL 分析对表、行和列进行统计信息。
表,视图,具体化视图和字典(带有CLICKHOUSE source_type)血缘
正在上传…重新上传取消

clickhouse-usage模板(统计ck使用详情)

能力

地位

笔记

数据探查   ✅  (可选)通过配置启用
检测已删除的实体   ✅  通过有状态引入启用
此插件具有以下功能 -

对于特定的数据集,此插件引入以下统计信息 -
前 n 个查询。
顶级用户。
数据集中每列的用法。
将这些统计信息按天或小时粒度聚合到存储桶中。
hive模板

能力

地位

笔记

域  ✅  通过配置字段支持domain
平台实例   ✅  默认启用
此插件提取以下内容:

数据库、架构和表的元数据
与每个表关联的列类型
详细的表和存储信息
通过可选的 SQL 分析对表、行和列进行统计信息。
4.2. RESET-API方式
手工构建元数据(即使表不存在)



API-MEDTADA人工构建模板
正在上传…重新上传取消

5. 血缘元数据
5.1 基于push机制构建血缘元数据
 SparkSql场景
自动解析sparksql依赖关系



 sparksql模板

正在上传…重新上传取消

SparkSession场景
自动解析sparkrdd/df依赖关系

sparkSession模板

spark = SparkSession.builder \
.master("spark://spark-master:7077") \
.appName("test-application") \
.config("spark.jars.packages","io.acryl:datahub-spark-lineage:0.8.23") \
.config("spark.extraListeners","datahub.spark.DatahubSparkListener") \
.config("spark.datahub.rest.server", "http://ipt:8080") \
.enableHiveSupport() \
.getOrCreate()

5.2 基于Rest API机制构建血缘元数据
RESET-API-LINEAGE DEMO
手工构建表与表之间血缘

 正在上传…重新上传取消

RESET-API-LINEAGE构建工具


正在上传…重新上传取消

 mr hql程序基于REST-API构建血缘(走pub_execute_sql脚本)


 mr hql回算程序基于REST-API构建血缘(走回算脚本)


 waterdrop  hive-ck REST-API 构建血缘(water drop脚本方式)


6. cli手工删除元数据


datahub delete --urn "urn:li:dataset:(urn:li:dataPlatform:clickhouse,DatabaseNameToBeIngested.add_record.product_user_new_20220117,PROD)" --hard(必须要硬删,软删可能会导致后续同名的表注册不进去)
删除开发环境中的所有数据集
datahub delete --env DEV --entity_type dataset
删除特定平台的所有容器
datahub delete --entity_type container --platform s3
删除开发环境中的所有管道和任务
datahub delete --env DEV --entity_type "datajob"
datahub delete --env DEV --entity_type "dataflow"
删除 PROD 环境中的所有 bigquery 数据集
datahub delete --env PROD --entity_type dataset --platform bigquery 
--hard
删除所有外观仪表板和图表
datahub delete --entity_type dashboard --platform finereport --hard
datahub delete --entity_type chart --platform finereport 
--hard
删除与查询匹配的所有数据集
datahub delete --entity_type dataset --query "_tmp" -n
7. 基于graphiql查询血缘以及上下游数量
172.16.8.69:9002/api/graphiql#,查询地址,更多文档详见DataHub GraphQL API | DataHub (datahubproject.io)

Demo
query{
dataset(
urn: "urn:li:dataset:(urn:li:dataPlatform:hive,cn_test_dim.biz_employee_account,PROD)" #查询的表
) {
lineage(
input: {direction: DOWNSTREAM, start: 0, count: 100, separateSiblings: true} #direction可选DOWNSTREAM或者UPSTREAM,start:从offset第几个起 count:返回结果数
) {
start #返回字段
count #返回字段
total #返回总共有多少条结果
relationships {
type
degree
entity {
type
urn
relationships(input: {types: [], start: 0, count: 100, direction: 查询该表关联的表,该层可以不查询
start
count
total
relationships {
type
entity {
urn
type
}
created {
actor
}
direction
}}}}}}}

查询表依赖上下游数量以及依赖明细UTILS


LineageUtils.py

# -*- coding: utf-8 -*-
from typing import List
import datetime
import datahub.emitter.mce_builder as builder
from datahub.emitter.mcp import MetadataChangeProposalWrapper
from datahub.emitter.rest_emitter import DatahubRestEmitter
from datahub.metadata.com.linkedin.pegasus2avro.dataset import (
    UpstreamLineage,
)
from datahub.metadata.schema_classes import *


class lineage_rest:
    def __init__(self):
        super().__init__()
        self.gms_server_url = "http://ip:8080"
        self.rest_emitter = DatahubRestEmitter(self.gms_server_url)
    def add_chart_lineage(self, input_datasets: List[str],dest_chart_name: str,description:str,emitter: DatahubRestEmitter,platform:str):
        last_modified = ChangeAuditStampsClass()
        chart_info = ChartInfoClass(
            title=dest_chart_name,
            description= description,
            lastModified=last_modified,
            inputs=input_datasets,
        )
        chart_info_mcp = MetadataChangeProposalWrapper(
            entityType='chart',
            changeType=ChangeTypeClass.UPSERT,
            entityUrn=builder.make_chart_urn(platform=platform, name=dest_chart_name),
            aspectName="chartInfo",
            aspect=chart_info,
        )
# emit 元数据,这是一个阻塞调用
        res = emitter.emit_mcp(chart_info_mcp)

    def add_dashboard_lineage(self, charts_in_dashboard: List[str], dest_dashboard_name: str,
                          description: str, emitter: DatahubRestEmitter):
        last_modified = ChangeAuditStampsClass()
        dashboard_info = DashboardInfoClass(
            title=dest_dashboard_name,
            description=description,
            lastModified=last_modified,
            charts=charts_in_dashboard,
        )

        chart_info_mcp = MetadataChangeProposalWrapper(
            entityType='dashboard',
            changeType=ChangeTypeClass.UPSERT,
            entityUrn=builder.make_dashboard_urn(platform="FineReport", name=dest_dashboard_name),
            aspectName="dashboardInfo",
            aspect=dashboard_info ,
        )
# emit 元数据,这是一个阻塞调用
        res = emitter.emit_mcp(chart_info_mcp)
#
    def add_guanbi_metadata(self, input_datasets: List[str],dest_chart_name: str,description:str, externalUrl: str,chartlUrl:str, emitter: DatahubRestEmitter, customProperties,platform:str,platform_instance:str):
        last_modified = ChangeAuditStampsClass()
        chart_info = ChartInfoClass(
            title=dest_chart_name,
            description=description,
            lastModified=last_modified,
            inputs=input_datasets,
            externalUrl=externalUrl,
            chartUrl=chartlUrl,
            customProperties=customProperties,
        )

        chart_info_mcp = MetadataChangeProposalWrapper(
            entityType='chart',
            changeType=ChangeTypeClass.UPSERT,
            entityUrn=builder.make_chart_urn(platform=platform, name=dest_chart_name),
            aspectName="chartInfo",
            aspect=chart_info
        )
# emit 元数据,这是一个阻塞调用
        res = emitter.emit_mcp(chart_info_mcp)
    def add_data_lineage(self, src_urns: List[str], dest_urn: str, emitter: DatahubRestEmitter,fineGrainedLineageList: List[FineGrainedLineageClass]):
# 构建数据血缘上流对象UpstreamClass实例
        upstream_tables: List[UpstreamClass] = []
        for urn in src_urns:
            upstream_tables.append(
                UpstreamClass(
                    dataset=urn,
                    type=DatasetLineageTypeClass.TRANSFORMED,
                    auditStamp=AuditStampClass(
                        time=int(datetime.datetime.now().timestamp() * 1000),
                        actor="urn:li:corpuser:datahub",
                    ),
                )
            )
# 构建上流数据血缘对象实例
        upstream_lineage = UpstreamLineage(upstreams=upstream_tables,fineGrainedLineages=fineGrainedLineageList)
# 构造一个MetadataChangeProposalWrapper对象
        lineage_mcp = MetadataChangeProposalWrapper(
            entityType="dataset",
            changeType=ChangeTypeClass.UPSERT,
            entityUrn=dest_urn,
            aspectName="upstreamLineage",
            aspect=upstream_lineage,
        )

# emit 元数据,这是一个阻塞调用
        res = emitter.emit(lineage_mcp)

    def add_data_metadata(self, urn: str, emitter: DatahubRestEmitter,gitUrl:str,hdfsUrl:str,azkaban_Url:str,description:str):
        print("git path{}".format(gitUrl))
        description = '<a href='+gitUrl+'>点击查看源代码</a>\n\r'+hdfsUrl+azkaban_Url+description
        dataset_properties = EditableDatasetPropertiesClass(
            description=description,
        )
# Construct a MetadataChangeProposalWrapper object.
        metadata_event = MetadataChangeProposalWrapper(
            entityType="dataset",
            changeType=ChangeTypeClass.UPSERT,
            entityUrn=urn,
            aspectName="editableDatasetProperties",
            aspect=dataset_properties,
        )
# Emit metadata! This is a blocking call
        res = emitter.emit(metadata_event)


    def add_colume_lineage(self, src_urns: List[str], dest_urn: str, emitter: DatahubRestEmitter):
            print(111)

if __name__ == '__main__':
# 构建一个GMS REST API Emitter。
    rest = lineage_rest()
    input_datasets: List[str] = [
        builder.make_dataset_urn(platform="clickhouse", name="dataset1", env="PROD"),
        builder.make_dataset_urn(platform="clickhouse", name="dataset1", env="PROD"),
    ]
    rest.add_chart_lineage(input_datasets=input_datasets,dest_chart_name="char1",description="测试chart222",emitter=rest.rest_emitter)

    charts_in_dashboard: List[str] = [
        builder.make_chart_urn(platform="superset", name="char1")
    ]
    rest.add_dashboard_lineage(charts_in_dashboard=charts_in_dashboard,dest_dashboard_name="dashbord1",description="测试dashboardt222",emitter=rest.rest_emitter)

# rest.add_data_metadata(urn=builder.make_dataset_urn("bigquery", "jjj"),emitter=rest.rest_emitter,externalUrl="https://gitlab.test.tech/big-data/dwh_finance/-/blob/master/ads/hql/ads_finance_batch_payment_detail.sql")
# rest.add_data_lineage(
#     # src_urns=[builder.make_dataset_urn("hive", "cn_test_dim.biz_activity_info"), builder.make_dataset_urn("hive", "cn_test_dim.biz_after_sale_order")],
#     src_urns=[builder.make_dataset_urn("bigquery", "jjj")],
#     dest_urn=builder.make_dataset_urn("bigquery", "zzz"),
#     emitter=rest.rest_emitter
# )


#LineageUtils.py

import argparse

import datahub.emitter.mce_builder as builder
import requests
from LineageDependencyUtils import LineageDependencyUtils
from datahub.metadata.com.linkedin.pegasus2avro.dataset import Upstream, DatasetLineageType, FineGrainedLineage, \
    FineGrainedLineageUpstreamType, FineGrainedLineageDownstreamType
from linage_rest import lineage_rest
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util import Retry
from requests.structures import CaseInsensitiveDict
from sqllineage.runner import LineageRunner
import pymysql
import time


class MrLineage(object):
    def __init__(self):
        super().__init__()
        args = self.load_parm()
        self.sql_file = args.sql  # 执行任务sql路径
        self.src_table = args.src_table  # 原始目标表
        self.des_table = args.des_table  # 目的表
        self.src_plate_form = args.src_plate_form,  # 来源平台
        self.des_plate_form = args.des_plate_form,  # 目的平台
        self.branch = args.branch,  # 目的平台
        if self.sql_file:
            project_path = self.sql_file.split("app/")[1]
            sql_path = project_path.split("/")
            self.project_name = sql_path[0]  # 项目名称
            self.git_path = "/".join(sql_path[1:])
        else:
            self.project_name = args.project_name  # 项目名称
            self.git_path = args.git_path

    @staticmethod
    def load_parm():
        """
        加载参数
        """
        parser = argparse.ArgumentParser(add_help=False)
        parser.add_argument('-sql', help='需要执行的sql', required=False)
        parser.add_argument('-src_table', help='原始目标表', required=False)
        parser.add_argument('-des_table', help='目的表', required=False)
        parser.add_argument('-src_plate_form', help='来源平台', required=False, default="hive")
        parser.add_argument('-des_plate_form', help='目的平台', required=False, default="hive")
        parser.add_argument('-branch', help='分支', required=False, default="master")
        parser.add_argument('-project_name', help='项目名称', required=False, default="")
        parser.add_argument('-git_path', help='sql git路径', required=False, default="")
        arg = parser.parse_args()
        return arg

    """
    根绝执行的hql解析原始表与目的表
    """

    @staticmethod
    def lineage_4_hql(hql_file=None, sql_str=None):
        """
        从静态sql语句解析血源
        """
        if not sql_str:
            with open(hql_file, 'r') as f:
                sql = f.read()
        else:
            sql = sql_str
        result = LineageRunner(sql=sql)
        return result

    def get_lineage(self):
        """
        获取血缘
        """
        result = self.lineage_4_hql(self.sql_file)
# 使用基础回算,用基础回算表做源表

        source_tables_list = list(map(lambda x: str(x), result.source_tables))
        target_tables_list = list(map(lambda x: str(x), result.target_tables))
        colum_lineage = result.get_column_lineage()
# 字段级血缘list
        fineGrainedLineageList = []

# 用于冲突检查的上游list
        upStreamsList = []

# 遍历列级血缘
        for columnTuples in colum_lineage:
# 上游list
            upStreamStrList = []

# 下游list
            downStreamStrList = []

# 逐个字段遍历
            for column in columnTuples:

# 元组中最后一个元素为下游表名与字段名,其他元素为上游表名与字段名

                # 遍历到最后一个元素,为下游表名与字段名
                if columnTuples.index(column) == len(columnTuples) - 1:
                    downStreamFieldName = column.raw_name.__str__()
                    downStreamTableName = column.__str__().replace('.' + downStreamFieldName, '').__str__()

# print('下游表名:' + downStreamTableName)
# print('下游字段名:' + downStreamFieldName)

                    downStreamStrList.append(self.fieldUrn(downStreamTableName, downStreamFieldName))
                else:
                    upStreamFieldName = column.raw_name.__str__()
                    upStreamTableName = column.__str__().replace('.' + upStreamFieldName, '').__str__()

# print('上游表名:' + upStreamTableName)
# print('上游字段名:' + upStreamFieldName)

                    upStreamStrList.append(self.fieldUrn(upStreamTableName, upStreamFieldName))

# 用于检查上游血缘是否冲突
                    upStreamsList.append(
                        Upstream(dataset=self.datasetUrn(upStreamTableName), type=DatasetLineageType.TRANSFORMED))

            fineGrainedLineage = FineGrainedLineage(upstreamType=FineGrainedLineageUpstreamType.DATASET,
                                                    upstreams=upStreamStrList,
                                                    downstreamType=FineGrainedLineageDownstreamType.FIELD_SET,
                                                    downstreams=downStreamStrList)

            fineGrainedLineageList.append(fineGrainedLineage)

        if len(target_tables_list) > 1:
            print("请注意,该组件不运行一个hql中执行多个任务,请拆分")
            raise Exception
        return source_tables_list, target_tables_list, fineGrainedLineageList

    """
    基于原始表和目的表构建并发送血缘
    """

    def rest_lineage(self, src_plate_form, des_plate_form, source_tables_list, target_tables_list,
                     fineGrainedLineageList):
        src_urns = []
        mysql_lineage_results = []
        plate_form = self.des_plate_form[0]
        project_id = ''
        project_name = self.project_name
        parent_project_name = ''
        table_name = target_tables_list[0].split('.')[1]
        full_table_name = target_tables_list[0]
        flow_id = ''
        job_id = ''
        table_exec_cmd = ''
        print('src_plate_form:' + src_plate_form[0])
        print('des_plate_form:' + des_plate_form[0])
        print('source_tables_list:')
        print(source_tables_list)
        print('target_tables_list:')
        print(target_tables_list)
        print('project_name:')
        print(self.project_name)
        dest_urn = builder.make_dataset_urn(des_plate_form[0], target_tables_list[0])
        for source_table in source_tables_list:
            table_level = target_tables_list[0].split('.')[0]
            parent_full_table_name = source_table
            parent_table_level = source_table.split('.')[0]
            src_urns.append(builder.make_dataset_urn(src_plate_form[0], source_table))
            print((plate_form, table_name, full_table_name, table_level, parent_full_table_name, parent_table_level,
                   project_id, flow_id, job_id, project_name
                   , table_exec_cmd))
            mysql_lineage_results.append((plate_form, table_name, full_table_name, table_level, parent_full_table_name,
                                          parent_table_level, project_id, flow_id, job_id, project_name
                                          , table_exec_cmd))
        ldu = LineageDependencyUtils(des_plate_form[0], target_tables_list[0], 3)
        lineage_description = ldu.getUPStreamLineageDepencies() + '\n' + ldu.getDownStreamLineageDepencies()
# print(lineage_description)
        self.branch = ['dev']
        print('branch:')
        print(self.branch)
        rest = lineage_rest()
        if self.sql_file:
            hdfs_Url = "<a href=http://nn1.test.cn:9870/explorer.html#/user/hive/warehouse/{}.db/{}>点击查看hdfs文件</a>\n\r".format(
                target_tables_list[0].split('.')[0], target_tables_list[0].split('.')[1])
# 根据表查询azkban调度
            exec_id, flow_id, job_id, start_time, end_time = self.execute_check_sql(target_tables_list[0].split('.')[1])
            azkaban_Url = "<a href='http://172.25.200.52:8082/executor?execid={}&job={}:{}'>点击查看azkaban调度日志</a>\n\r".format(
                exec_id, str(flow_id).split(':')[0], job_id)
            cost_time = format((end_time - start_time) / 1000 / 60, '.3f')
            start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(start_time / 1000))
            end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time / 1000))
            lineage_description = '最近一次开始运行时间:{},结束时间:{}.耗时{}分钟'.format(start_time, end_time,
                                                                                          cost_time) + '\n' + lineage_description
        else:
            hdfs_Url = ''
            azkaban_Url = ''
        rest.add_data_metadata(urn=dest_urn, emitter=rest.rest_emitter,
                               gitUrl="https://gitlab.test.tech/big-data/{}/-/blob/{}/{}".format(self.project_name,
                                                                                                    self.branch[0],
                                                                                                    self.git_path),
                               hdfsUrl=hdfs_Url, azkaban_Url=azkaban_Url, description=lineage_description)
# 血缘写入datahub
        rest.add_data_lineage(
            src_urns=src_urns,
            dest_urn=dest_urn,
            emitter=rest.rest_emitter,
            fineGrainedLineageList=fineGrainedLineageList
        )
# 血缘写入mysql
        self.add_data_lineage_to_mysql(mysql_lineage_results=mysql_lineage_results,
                                       target_table_name=target_tables_list[0])

# 血缘往mysql也写一份便于查询
    def add_data_lineage_to_mysql(self, mysql_lineage_results: [], target_table_name):
# 连接数据库
        conn = pymysql.connect(host=''  # 连接名称,默认127.0.0.1
                               , user=''  # 用户名
                               , passwd=''  # 密码
                               , port=3309  # 端口,默认为3306
                               , db=''  # 数据库名称
                               , charset='utf8'  # 字符编码
                               )
        cur = conn.cursor()  # 生成游标对象
        insert_sql = (
            "insert into table_lineage (plate_form,table_name,full_table_name,table_level,parent_full_table_name,parent_table_level,project_id,flow_id,job_id,project_name,table_exec_cmd)"
            "values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s) on duplicate key update plate_form  = values(plate_form), table_name = values(table_name),full_table_name = values(full_table_name),table_level = values(table_level),parent_full_table_name = values(parent_full_table_name),parent_table_level= values(parent_table_level),project_id= values(project_id),flow_id = values(flow_id),job_id  = values(job_id),project_name           = values(project_name),table_exec_cmd         = values(table_exec_cmd)")  # SQL语句
        print(insert_sql)
        cur.executemany(insert_sql, mysql_lineage_results)  # 执行SQL语句
        update_sql = (
            "update  table_lineage set parent_project_name='{}' where parent_full_table_name='{}'".format(
                self.project_name, target_table_name))  # SQL语句
        print(update_sql)
        cur.execute(update_sql)
        conn.commit()
        cur.close()  # 关闭游标
        conn.close()  # 关闭连接

    def execute_check_sql(self, project_name):
# 连接数据库
        conn = pymysql.connect(host='1'
                               , user=''  # 用户名
                               , passwd=''  # 密码
                               , port=3309  # 端口,默认为3306
                               , db=''  # 数据库名称
                               , charset='utf8'  # 字符编码
                               )
        cur = conn.cursor()  # 生成游标对象
        sql = "select exec_id,flow_id,job_id,start_time,end_time from execution_jobs where  job_id like '%{project_name}%' and start_time>(UNIX_TIMESTAMP(CAST(SYSDATE()AS DATE))*1000) ORDER BY exec_id DESC limit 1".format(
            project_name=project_name)  # SQL语句
        print(sql)
        cur.execute(sql)  # 执行SQL语句
        data = cur.fetchall()  # 通过fetchall方法获得数据
        exec_id = ''
        flow_id = ''
        job_id = ''
        start_time = 0
        end_time = 0
        if len(data) > 0:
            exec_id = data[:1][0][0]  # 打印输出前2条数据
            flow_id = data[:1][0][1]  # 打印输出前2条数据
            job_id = data[:1][0][2]  # 打印输出前2条数据
            start_time = data[:1][0][3]  # 打印输出前2条数据
            end_time = data[:1][0][4]  # 打印输出前2条数据
            if end_time == -1:
                end_time = time.time() * 1000
                print("end_time:{}".format(end_time))
        cur.close()  # 关闭游标
        conn.close()  # 关闭连接
        return exec_id, flow_id, job_id, start_time, end_time

# 库名设置
    def datasetUrn(self, tableName):
        return builder.make_dataset_urn("hive", tableName)  # platform = hive

# 表、列级信息设置
    def fieldUrn(self, tableName, fieldName):
        return builder.make_schema_field_urn(self.datasetUrn(tableName), fieldName)

    def run(self):
        """
        执行过程
         1. 解析hql血缘
        """
        global source_tables_list, target_tables_list, fineGrainedLineageList
        if self.sql_file:
            try:
                source_tables_list, target_tables_list, fineGrainedLineageList = self.get_lineage()
            except:
                print("获取血缘失败")
        else:
            source_tables_list, target_tables_list = self.src_table.split(","), [self.des_table]
            fineGrainedLineageList = []
        try:
            test_url = ""
            headers = CaseInsensitiveDict()
            headers["Content-Type"] = "application/json"
            rq = requests.Session()
            rq.mount('https://', HTTPAdapter(max_retries=Retry(total=0)))
            resp = rq.get(test_url, headers=headers, timeout=2)
            print("test status_code:")
            print(resp.status_code)
            if resp.status_code == 200:
                self.rest_lineage(self.src_plate_form, self.des_plate_form, source_tables_list, target_tables_list,
                                  fineGrainedLineageList)
                print("发送血缘成功")
            else:
                print("服务器状态异常不发送血缘")
        except Exception as ex:
            print(ex)
            print("test status:服务器报错不发送血缘")
            print("发送血缘失败")
        print("血缘运行结束")


if __name__ == '__main__':
    lineage = MrLineage()
    lineage.run()

​
作为 DataHub 的专家,我需要再次纠正一下之前的回答。DataHub 和 DolphinScheduler 是两个不同的开源项目,它们虽然都是与大数据相关的项目,但是功能和用途有所不同。接下来我将为您介绍开源的 DataHub 元数据工具如何获取 DolphinScheduler 中的数据血缘关系,并给出一个具体的案例。 开源的 DataHub 元数据工具是一款基于 DataHub元数据管理工具,可以自动化地收集和管理数据的元数据信息。其中,元数据管理包括数据血缘关系的记录和展示。具体步骤如下: 1. 在 DataHub 中创建需要记录数据血缘的 Topic,并设置需要记录血缘信息的字段。 2. 在 DolphinScheduler 中创建数据血缘任务,指定需要记录数据血缘的数据源和表,并执行任务。 3. 在 DataHub 元数据工具中添加 DolphinScheduler 的元数据源,配置数据血缘任务的信息。 4. 开启元数据采集任务,DataHub 元数据工具会自动从 DolphinScheduler 中获取数据血缘信息,并保存到元数据库中。 5. 在元数据工具中查看数据血缘关系,可以查看到每个数据的来源和去向,以及数据在流程中的处理状态和时间。 下面是一个具体的案例,假设我们有一个需求,需要实时统计某个数据源中数据的血缘关系,以及数据在流程中的处理状态。 1. 在 DataHub 中创建一个 Topic,名为 data_topic,并设置需要记录数据血缘的字段。 2. 在 DolphinScheduler 中创建数据血缘任务,指定要记录数据血缘的数据源和表。 3. 在 DataHub 元数据工具中添加 DolphinScheduler 的元数据源,配置数据血缘任务的信息。 4. 开启元数据采集任务,DataHub 元数据工具会自动从 DolphinScheduler 中获取数据血缘信息,并保存到元数据库中。 5. 在元数据工具中查看数据血缘关系,可以查看到每个数据的来源和去向,以及数据在流程中的处理状态和时间。 通过以上步骤,我们就可以实时获取 data_topic 中数据的血缘关系,以及数据在流程中的处理状态。同时,DataHub 元数据工具可以自动化地管理数据的元数据信息,提高数据管理的效率和准确性。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值