目录
原文大佬介绍的这篇数据血缘架构设计及应用有借鉴意义,现摘抄下来用作沉淀学习。如有侵权,请告知~
一、背景和目标
1.1 数据平台
腾讯欧拉数据平台,是一款基于DataOps理念,实现生产即治理的一站式数据平台,主要包括三个子产品:
- 资产工厂:负责整体的数仓建设,数仓模型的开发;
- 欧拉的治理引擎:负责全链路成本的数据治理;
- 数据发现:负责元数据的管理;
数据血缘是欧拉的一个子模块,直接服务于以上三个子产品,也是本次分享的主题。
1.2 为什么要做数据血缘
为什么要做数据血缘?主要有两个原因,一个是现状不能满足血缘数据需求,另一个是希望以血缘为基础做更多的事情。
之前只能拿到离线内部表级别的数据血缘,拿不到埋点下发到数据管道再接入离线数仓这种链路的血缘,因此整体覆盖度不够。同时血缘的粒度不够细致,之前拿到的是表级别的血缘,需要字段级的血缘。还有一个比较重要的问题是缺少血缘图谱挖掘计算模型,包括算法库。
另一方面,希望能够以血缘为基础做更多的事情,包括全链路的数据治理,指标的全链路规则,血缘的成本洞察,以及基于血缘的一些数仓开发,这些问题和需求共同催生了欧拉血缘的建设。
1.3 目标和现状
血缘的建设包括两个方首先是提升数据血缘的广度,涵盖从数据生产面,、数据加工、到数据应用的全链路。包括数据生产环节的腾讯灯塔、大同,数据加工环节的欧拉平台,以及数据应用环节的DataTalk 报表、TAB 的 ABtest等等,已覆盖20+产品,形成了非常完整的全链路资源。
另一方面,提升数据血缘的深度,做更精细化的血缘建设。除了任务血缘之外,还有表血缘,字段血缘,目前字段血缘是最深的层级。
任务血缘的主要工作是打通各平台产品间的任务级别抽象,实现全链路,跨平台的完整的任务血缘关系图谱,覆盖了内部多种离线和实时的数据产品。
表血缘是要打造全链路血缘数据图谱,包括各种表级别的抽象,比如离线Hive表,Mysql关系库的表,还有OLAP、Impala等等,消息队列也被定义为表级别的抽象。
在表血缘的基础上,会把血缘粒度拓展至字段级别,目前已经完成了离线数仓内部SQL任务的字段血缘建设。如果不考虑非SQL的任务(包括jar包任务或Spark任务),字段级血缘会产生断层,我们的策略是以表血缘为基础,上下游进行全量依赖。假设下游表的每个字段都依赖于上游表的全部字段,可以把整个字段血缘串起来,避免中间产生断层。
数据血缘有着非常多的应用场景,包括欧拉血缘查询服务,还有很多基线项目、成本分摊项目以及各种数据治理项。覆盖的业务包括整体 PCG 的 ToC 业务,比如 QQ、腾讯视频、腾讯新闻、腾讯看点、微视、应用宝等。
二、项目架构
2.1 项目架构选型
首先进行了选型工作。第一个考察的产品是 Apache的Atlas,这也是目前使用率最广的数据血缘开源产品,但在实际应用汇总存在着一些问题,比如Atlas只有表级血缘,字段血缘不完善,且对大数据引擎的依赖比较强,关系扩展比较麻烦,需要比较多的二次开发。另一些产品,如OpenMetadata、Datahub等对血缘的支持比较弱,更侧重于元数据管理和数据发现的服务,Metacat、Amundsen基本上只有数据发现功能。
基于以上原因,最终选择基于腾讯内部产品的生态自建血缘架构,例如EasyGraph 图数据库和成熟的ES产品,还有分布式的基于内存的NoSQL服务Meepo 等等。
2.2 项目架构图
上图展示了整体的项目架构。最底层 UniMeta是欧拉内部的元数据管理平台,包含数据的接入和导出。血缘建设是基于UniMeta接过来的一些基础数据,比如Yarn日志,从中解析出读表和写表。UniMeta中还包括经常用到Hive的hook机制,我们也会通过UniMeta同步一些产品配置,比如离线表导出到MySQL、Impala、ClickHouse,基本上都是通过产品配置来进行血缘的解析。还有其它一些产品会发来内部血缘的消息队列。极端情况下,比如 jar 包任务,会有一些手工填报的血缘。整体上包括离线、实时和接口三种数据类型。
在UniMeta基础之上,有血缘ETL逻辑加工,常规数仓的建模和加工环节,包括数据清洗,血缘淘汰,血缘融合等。血缘关系整体上分为两类,一是血缘关系,即存在实际数据流转关系;另一种是我们建立的对应关系,比如表和任务之间或表和字段之间的对应关系。
其中一部分数据会通过SQL解析框架,进行SQL层级的血缘解析,最终把解析好的数据输出到ETL。SQL解析框架,底层是自研的SQL解析引擎,附带代码格式化。还有基于AST的算法库,实现AST级别SQL的用户操作。
在ETL基础之上,有全链路的血缘数据质量监控,会生成血缘整体的监控报表。目前,表级血缘的层级有40+层。这里会催生出一些数据治理项,在正常的数仓建模情况下,应该不会有这么高的层级,会把高层级异常的血缘推送给用户,进行血缘的压缩,优化整个数据链路。另外,还会对表的上下游数量进行监控。
在血缘基础之上,基于GraphX构建了图算法库,因为整体的AST血缘是一个树形结构。利用GraphX的迭代计算功能,最终形成精细化的血缘建设,包含任务级血缘,表级血缘,字段级血缘,数值血缘,还可以自定义血缘图谱,这些都会存储到图谱数据库中。
底层的数据结构包含EasyGraph,即图数据库,ES用于节点属性的模糊检索,Mysql用来存储元数据信息,Redis提供预计算的功能,GraphX把血缘的全部下游整体计算好之后,数据以KV的形式放在Redis中,增加血缘查询的效率以及接口访问速度。另外还会把血缘在离线仓TDW备份。
依托血缘数据,以REST APIS的形式对外提供统一的血缘服务,包括数据检索服务,血缘查询服务,路径查询服务。
通过UniMeta 的智能网关和权限管理对外赋能,目前更多的是血缘在内部的应用,包括数据治理、成本血缘建设,基线项目,数仓开发,血缘查询。
三、模块化建设
接下来介绍每个子模块的细节。
3.1 统一实体UID规范
首先是统一的实体UID 规范。因为图谱包含不同类型的节点,需要设计一套统一的UID规范。包含表,字段,任务,消息队列、NOSQL(Redis或者Hbase),还有HDFS的路径和API接口,都会纳入到全链路的血缘数据图谱中,目前已经接入40多个实体类型。
这里设计了一套UID的生成规则,参考了URL 的设计理念,其中 product 对应的是 scheme,因为腾讯内部通过规划产品和运营产品来唯一定义一个项目,所以使用 product 对 UID进行唯一确定。在产品之下会有各种各样的类型,type 字段对应 URL 的 host:port,再通过属性来唯一定义一个实体类型。在 ID的基础之上,基于 MD5的hash 算法生成唯一的 UID,再通过UID存储到图库里。
此外还做了点边解耦的操作,通过对点,边进行分别的维护。其中点的维护者,与边的维护者可以是不一样的,这可以保证点属性的统一,避免重复接入,还可以提供比较好的灵活性和扩展性。
3.2 血缘边的建设
血缘的边建设采用了化整为零的策略。例如离线数仓内部表和表之间就是原子关系,表导出到 MySQL 或 ClickHouse也是一种原子关系。关系分为两种类型,一种是存在数据流转的关系,这个关系其实就是普通意义上的血缘关系,比如任务血缘,表血缘,字段血缘。另外一种关系就是对应关系,比如表和任务的对应关系,任务和实例的对应关系。
构建了原子关系之后,就可以对这些原子关系进行自由的组合去生成自定义的血缘图谱,并提供了图谱路径的查询接口,上图右侧自定义血缘的接口,可以通过任务找到其下游任务,再通过下游任务找到对应的表,然后再通过这个表找到对应的下游表,最后找到对应的DHFS,形成一种路径查询。
3.3 SQL解析框架
上图是整体的SQL解析框架,目前大部分的大数据引擎SQL解析都是基于Calcite和Antlr。有这个信息之后,并不是直接对SQL进行解析,而是对Calcite和Antlr生成的语法树进行转换操作,转换成自研的AST语法树,会提供一个非常方便的转换模块,相应的有一些完善的debug机制,还有类似Calcite的语法扩展功能。
AI 自研的语法结构,相比原生的 Calcite 会有一些优势。在某些特定的场景下,会打通和图库的融合,在图库里存储数仓所有 SQL 任务,这样做的好处在于,比如我们有一个AST算法库,从SQL中选择一个字段,把所有相关联的逻辑取出来,再组合成线上可以执行的SQL,就会形成AST级别的血缘。也有基于AST对SQL的转换,还有代码可视化的模块,比如可以按照 PCG 内部 SQL 的格式化标准,用户不需要再关注代码格式,只需要通过公用的工具来进行格式化,特别是在进行code review的时候。以UDF的形式对外提供后端的接口服务,还有SQL的可视化服务。
3.4 图算法库
图算法库是比较有特色的一个功能,基于 Spark 的GraphX 来进行图计算。目前已经落地的场景,包括血缘数据探查,例如上下游的最大深度,还有层级的分布情况以及血缘环路的检测,我们会进行相应的检测,推送给业务进行数据治理。
此外,还有数据治理的挖掘项,包括常见的冷表冷字段的下线,耗时最长链路的优化。第三块,会用 GraphX 做血缘的预计算,如果只从图库进行血缘的上下游查询,有时候它的接口响应速度不是那么理想,会用GraphX 做一个图计算,然后把数据放在 Redis 里,提升接口的访问速度。
3.5 全链路血缘数据质量
在构建血缘的过程中,会做全链路血缘数据质量的监控和探查。前文中提到了血缘整体的数据监控报表,再来介绍一下数据一致性的保证,采用了 Lambda 架构来构建血缘数据。在此过程中,需要保证两方面的数据一致性,第一个是离线和实时数据的一致性(因为离线和实时是两条链路),第二是需要保证图库和离线数据的一致性,离线数据在导入图库的时候可能会产生数据上的偏差。之所以采用 Lambda 架构是因为目前血缘数据仍然以离线数据为准,实时的血缘不能覆盖全部的血缘关系。
图计算和挖掘任务是依赖于离线数据的,上图是保证一致性的架构。第一步实时任务正常写入 todo。第二个流程,是来解决离线和实时数据的一致性,在 2.1 的时候,会向实时任务发送消费暂停的信号,实时任务就会停止写入图库,再用离线数据去更新图库里的数据,这样来保证离线和实时数据的一致性。第三个流程是用来保证图库和离线数据的一致性,采用图库数据进行一个 dump操作,dump 之后再和离线数据进行对比,将差异向图库同步。在第四步,向实时任务发送正常消费信号,任务就可以正常进行消费。
3.6 统一血缘服务
通过 REST API 对外提供统一的血缘服务。首先数据检索是直接基于图数据存储的 ES 数据,目前可以进行任务节点、表节点、指标的模糊检索。
实时血缘查询是基于内部 EasyGraph 图库,来进行实时的血缘查询,包括上下游的多层级血缘查询、血缘可视化的交互与展示,这边也会提供预计算的血缘查询服务,首先使用 GraphX 进行血缘的预计算,比如刚才提到有 4 万多个下游的节点,如果图库好的话需要几分钟,这在产品级别是不能接受的。这种情况下,我们就会使用预计算的数据,响应时间就会压缩到几十毫秒。
在统一血缘服务基础之上,会通过智能网关和权限管理进行对外赋能,另一方面我们用内部应用直接进行调用。
四、应用
最后介绍血缘建设的内部应用场景,整体分为五大类。
- 首先是数据治理,血缘最直接的应用就是应用在数据治理上,包括冷表、冷字段、甚至HDFS 冷数据的下线,还有空跑任务,空跑逻辑的下线,耗时最长链路的优化等等。
- 第二是血缘查询,直接面向用户进行血缘查询;
- 第三是数仓开发,支持SQL代码的可视化,逻辑资产管理,低效、无效代码的检测,因为是 AST 级别的血缘,所以能够有效地把低效和无效代码检测出来。另外,会基于AST的血缘进行微数仓相关的探索。
-
第四是基线项目,对全链路任务的失败或者延迟进行监控。
-
最后是全链路血缘成本洞察项目,结合血缘和成本核算两块内容。
4.1 数据治理
数据治理,整体按照数据治理的难度分为浅水区和深水区。浅水区包含表热度和字段热度,低热度或者无热度的冷表下线、冷字段下线和冷数据下线,再往下一层会有空跑任务的下线和空跑逻辑的下线,如果某个字段判定为冷字段,那么这个字段的所有加工逻辑就会被判断为空跑逻辑,我们会把这些信息给给用户,让用户进行SQL级别的下线操作。
深水区包括重复计算的判断,也是基于AST级别的血缘建设,最终希望形成表ROI,字段ROI还有指标ROI的建设,目前还在开发中。
4.2 全链路血缘成本洞察
之前的成本核算和分摊不够精确,把中台或者上游产品的成本向业务分摊,目前参照的是用量,但是用量难以准确反映出下游实际使用了多少成本。通过把成本和血缘进行结合,能够把每个上游表的成本向每一个下游节点进行分摊,整体构成了全链路血缘成本的链路。
上图是整体的构建思路,首先第一层有a,b,c,d,e,f 6个节点,每个节点都有节点都有对应的成本,逐层进行分摊,第一层a节点会向它的下游b和c分别分摊 100/2 ,会产生50的成本,低的节点目前就会有150的成本,依次再进行第二层,第三层的分摊,这样把所有节点的成本分摊到全部的叶子节点。不一定所有的叶子节点都是我们的成本节点,例如原子指标和派生指标,可能派生指标的表是基于原子指标表来进行开发的,但是原子指标和派生指标都会作为成本节点来进行分摊。
4.3 全链路成本血缘洞察
第二个问题就是成本逐层向下分摊时的权重问题,目前我们采用的是向下游平均分摊成本的方式,各个子节点的权重是 n 分之一,但很多业务提出这样做是有问题的,理想的方式是按照下游的实际使用量,包含字段维度和记录数的维度,字段维度就是下游使用了上游表的多少个字段,然后记录数维度就是下游在它的 where 条件中用这个条件查出来了多少记录数,目前还在研究与开发中。
还有环路问题,稍微有些复杂,考虑了几种方案。首先把环路进行拆解,从 c 到 a 就不会去分担成本。第二个方案是是无限循环的分摊方案,它会让环路进行无限循环,最终它会在每一个节点上分摊到无限循环之后的成本,其实可以通过无穷级数来事先预计算,不需要无限循环,只需要循环一次,乘以无穷级数的求和的结果就可以了。不过最终采用的是环路的整体判断方案,把 a、b、c 作为环路的整体,然后再向 d 进行分摊。
第四个是图计算的性能问题,利用GraphX进行图计算,迭代次数过多,会有数据倾斜的问题,综合采用了多种方案来进行性能优化。实际应用可以使成本治理,从单点扩展到全部链路,成本分摊的逻辑,更加符合数据加工的逻辑。可以帮助业务直接观察到自身的成本来源,帮助上游产品(数据中台)向下游分摊自身的成本。另外会针对单个节点,给出对应的成本优化建议。最终希望以血缘成本为基础,进行数仓的全局优化。
4.4 全链路成本血缘洞察
最后是基线项目,对整个任务链条进行监控和保障。具体实现过程为,A、B、C、D 是正常的数据链路,比如D是保障任务,某一 B 任务延迟,导致其下游延迟。在基线项目中有一个水位线的概念,与Flink处理延迟消息的水位线比较类似,因为B的延迟导致了D 的水位线超过了预警线,在这种情况下,会在整个链条上进行预警,这只是单个任务。比如D任务可能会有其他的路径,它的每一条路径都会推高D的水位线,如果超过预警线就会进行监控预警。
上图是基线项目的整体结构,可以实现全链路的可追踪、可预警和可归因,比较核心的服务,例如完成时间的预测,动态最长路径的推送,历史最长路径的计算,会以两种形式推送给用户,第一种是企业微信以文本形式来推送,第二种是通过一个平台,以甘特图的形式展示出全链路整体的延迟或者监控情况。
五、FAQ
Q1:从头开始血缘关系建设的话,是从任务到字段建设好,还是从字段到任务建设好?
A1:建议从任务到字段,表血缘需要和任务血缘进行对齐操作,字段血缘也需要和表血缘进行对齐操作。还有一个情况,目前任务级血缘是最准的,因为任务血缘是用户进行线上的任务增删改查的操作,下发的消息或者 MDB 数据,所以推荐从任务血缘向表血缘和字段血缘进行向下建设。
Q2:怎么评估血缘的准确性?
A2:有一个内部监控,这个监控只能去监控解析的成功率和一些比较固定的形式。目前采用的方法还是结合固定监控和人工探查。比如字段血缘,其实目前的解析准确率已经很高了,能达到 99% 以上,但是 99% 这个数值,也是通过人工选取一些样例数据,对比解析的结果和人工判断的结果,得到额准确率。
Q3:在读写数据的时候用的 JDBC 或者自定义的data source,怎么把这些血缘串联起来?现在是否支持?
A3:目前能够收集到的就是任务的配置, JDBC目前还没有介入到这一块,因为可能涉及到jar 包任务。针对jar 包任务,会进行上下游表级血缘的全量依赖,并对字段级别进行存量依赖。我们会分两个概念,表级别的可以通过配置获取到,字段级别就会使用表级别的血缘进行上下游的存量依赖。
Q4:基线项目的用户都有哪些角色?
A4:基线项目前主要面对的是 DE 和 DS,即数据工程师和数据分析师,保证任务和指标能够正常产出。他们会关注任务今天为什么会延迟,会逐层地往上找,通过基线项目及时给他发一个预警,通知他们链条中哪一个节点出了延迟或者出了问题,方便快速定位排查。
Q5:主要用的数据结构和算法?
A5:SQL 解析框架用到的数据结构和算法会比较多,因为这个是纯自研的 SQL 解析框架,并且它和普通的 SQL 解析框架还不太一样,它不是直接去写 SQL 的 Parser,而是基于 Calcite 和 Antlr 解析好的 AST 语法树,进行转换操作,转换到自研的语法结构。数据结构主要是递归和回溯,另外有比较细节的算法,处理某个问题节点会应用到特定的算法。
参考文章: