元数据是数据平台的衍生数据,比如调度任务信息,离线hive表,实时topic,字段信息,存储信息,质量信息,热度信息等。在数据平台建设初期,这类数据主要散落于各种平台子系统的数据库中,例如HiveMetaStore,调度系统db等,在这个时期数据平台主要以服务业务数据需求为主,平台也以管理表,写ETL,配置调度这类功能性需求作为重点,对于这些散落元数据的收集与统一管理并没有太过强烈的诉求。
随着数据平台业务规模的增长,平台会沉淀大量的数据表,调度任务等元数据。由于前期快速的业务发展产生大量数据管理成本,存储计算成本。此时会逐步产生诸如模型规范治理、模型变更影响,指标异动定位,重复建设治理等需求场景。基于这些场景需求,此时数据平台仅提供数据开发相关的功能便难以满足业务需求,需要建设以数据地图(找数),血缘地图(定位数据链路),影响分析工具,资产看板,治理工具 等一系列偏向于事后的信息查询、治理相关产品工具。
由于先前元数据的散落,导致系统间数据相互耦合,边界不清楚,无法以全局视角观察分析平台数据资产,无法串联数据之间的生产加工关系。于是建设起完善可靠的元数据服务成为后续满足数据发现,数据治理业务的关键。
元数据基建
背景&目标
B站的数据平台元数据建设之初,由于对元数据的业务理解不够深入,人力投入有限,实现方案采用的是针对特定需求深度定制化。比如需要某类Hive表的字段信息,那么就针对这个场景,设计一批hive表与字段的元数据表,通过直连HMS拉全量数据,定制业务逻辑消费HMS的Binlog进行变更同步,再通过暴露一批查询表字段的HTTP接口,提供给需求方进行查询。
基于这种模式,虽然短期也能满足需求,但是暴露出了两个大问题:1. 灵活性差,实现非常定制,难以支持频繁出现的边界场景,只能再针对新需求做排期开发,严重拖慢业务迭代速度 2. 开发维护成本高,大量定制的采集逻辑、异构的元数据表、支持各种业务场景的接口,在有限的人力资源上难以支撑,还要随时面对元数据模型变更的问题,采集质量的问题。
在这种状态下,也出现了一些必然结果,由于无法快速支持业务需求,需求方通常会自建离线元数据来跑通业务,产生了重复建设和后期治理的问题。由于开发维护成本高,支持元数据业务的同学疲于应对各种需求,压力大,还要兼顾各类线上的元数据质量问题排查运维。
所以,体系化建设元数据的目标之一就是统一元数据。即以统一的元数据模型,统一的采集方式,统一的存储方式,统一的查询方式支撑上层元数据业务需求。
系统总览
统一元数据-模型
元数据模型需要满足3点要求:
统一标识元数据资源
描述所有类型的元数据资源
描述上述各类元数据资源之间的各种类型关系
我们在这部分借鉴了业界的一些通用方案,以标识协议URN+实体+关系进行了统一元数据模型的构建。
统一标识协议URN
URN = 协议域 + 业务域 + 资源类型 + 唯一资源ID
每个域之间以 「:」进行分隔。
其中协议域全局固定为urn;对于数据平台内部的资源业务域统一为datacenter;资产类型为协商约定,由此文档统一管理;唯一资源ID则由各个资产的定义方自行约定。
基于URN协议,我们已经约定了16类的资源类型,以下列举几类作为示例:
这里针对最重点的资产 - 表的URN定义展开讨论一下,我们认知中的表,可以来源于平台内部,比如最常见的Hive,ClickHouse表等,也可以来源于平台外部,比如业务的Mysql,TiDB,还有一些是针对类似KV结构映射出的逻辑表。
由于在血缘场景中,我们需要打通这些跨域类型的数据表的关系,所以需要站在全局的视角对他们进行统一标识。我们采取的方案,使用了tab作为这些数据表统一类型,再以源.库.表三段式作为唯一资源ID对各类数据源的进行表述,引申到字段同理,是以源.库.表.字段四段式进行表述。
需要注意的是,如果要使用这种表达方式,必须满足一个前提:具备统一的数据源管理,保障相同来源的数据源名称唯一且不发生变更,比如使用同一个mysql集群下的数据库中的表,必须在全部业务流程中,收敛为使用同一个数据源。这里会涉及到了关于数据源命名规范的问题,不多做展开。
实体关系模型
上图的模型中大部分还是比较好理解的,但有以下两个概念特别讲解一下。
实体的Aspcet
在通常的理解中,一个实体的全部信息应该来源于一个系统,这样当进行一类资源的采集时,我们只需要找那个系统去同步,但实际会存在一些特殊情况。比如,一张Hive表,它的基础属性都存于HMS之中,但是围绕着Hive表,会建设很多衍生服务,这些服务会单独管理一些衍生的业务属性,例如Hive表的生命周期、安全等级等。
针对同一个实体,它的属性来源分散的情况,我们借鉴了Linkedin开源元数据平台DataHub中的设计,引入Aspcet(切面)概念,对来源不同的属性进行区分。Aspcet在模型中的作用,更重要的是用在元数据采集时,这部分会在后面采集内容说明。
关系的BuilderURN
在维护关系数据时,我们常会遇到一个问题,关系是由谁来构建的。比如离线的表级血缘中,血缘关系通过调度任务来构建,此时血缘的生命周期也应该跟随相应的任务。针对类似场景,我们在关系模型中加入了builderURN作为抽象,也就是构建关系的实体URN,这样我们将任务的URN置于builderURN属性中,而不是作为输入输出中的一个点。这样做有几点好处:
减少关系数据,降低查询复杂度:如果将任务作为关系的一个点,构建表级血缘,要么做实时的跨层查询,要么需要冗余维护额外的数据。
方便生命周期管理:当任务被下线时,我们可以快速查询到由该任务构建的关系,级联进行删除操作。
统一元数据-采集
元数据的采集部分主要涉及几点问题,其中包含技术问题,也包含职责分工边界的问题。
采集方式选型
对采集方式的选择,一般会比较几种方案:
批拉取
采集侧进行调度触发拉取,业务侧支持按业务偏移量进行增量查询。优点:采集配置可控,易监控和运维。缺点:业务侧需要配合进行定制取数逻辑开发,对业务数据的存储更新方式有一定要求。
批上报
业务侧自行调度,按业务偏移量增量查询后自主上报,采集侧被动做消费。优点:整体采集逻辑简单,开发成本低。缺点:无法控制采集配置(频率、间隔),采集问题难监控、难定位,难运维。
埋点上报
业务侧将上报埋点到数据变更流程中。优点:实时性强,对业务数据的存储更新方式无特定要求。缺点:采集问题难监控、难定位,几乎无法运维。
这里我们选型是1和3,权重倾向于可控采集和采集质量保障,对于需要强保障质量的类型,我们主推采用1的方式做采集。对于一些非核心数据,或者存储更新不规范,无法批量取数的场景,也可以选用3的方式由业务自行上报。
业务逻辑谁来维护
为了解藕业务,降低元数据去理解业务含义,维护业务变更等等成本,我们约定统一由数据源头业务负责维护数据模型到统一元数据模型的转换逻辑,也就是说,无论是自助上报,还是接口拉取,我们都会以统一的元数据模型来进行数据交换,避免产生业务逻辑处理各类异构数据。
采集质量保障
采集质量保障是非常重要的一环,直接关系到后续元数据上层业务能否有效开展。在采集质量方面,我们踩过很多坑,比如业务侧硬删数据、业务侧数据事务落库问题、业务侧上报bug、消息中间件不稳定等导致最终数据不一致,且缺少有效的数据监控,定位处理成本非常的高。
基于这些问题,我们建设落地了成元数据质量保障机制,核心思路是以单批次检查和全局兜底检查作为质量问题的发现定位手段,以业务实现规范取数接口支持了采集全量拉取、采集增量拉取、运维补数拉取和运维靶向拉取,作为问题处理手段。最终做到自动化的完成采集质量问题发现、定位、处理整套运维动作。
统一元数据-存储
TIDB - 元数据DB,承载采集到的实体关系数据,作为元数据业务的中心存储。
ES - 查询搜索DB,数据从TIDB的实体表同步,提供元数据检索能力,提供跨源跨表join,分词查询,权重控制,自定义词包等能力。
HugeGraph - 关系搜索DB,数据从TIDB的关系表同步,提供图结构下的深度遍历,路径选择,成环处理等能力。
统一元数据-查询
在元数据查询的场景中,有非常多的定制需求,不仅要满足上层应用对元数据的查询,也要满足来自用户和数据治理层面的突发需求。所以在元数据查询能力建设上,既需要具备通用性,支持各种灵活的查询情况场景,又需要具备可复用性,避免重复建设导致维护成本的上升。
因此我们采用了通用元数据查询的设计思路,查询底层依赖上面Tidb、ES、图数据库的搜索能力。通用查询主要设计了两个核心接口,通用实体查询和通用关系查询,并逐步将上层应用查询使用进行收敛。
通用查询接口的设计中,我们实现了两个重要的功能降低使用成本,提高灵活度 1. 类SQL查询 2. 关联查询
为了使用上的便捷性,我们定制了一个SQLParser的实现,适配SQL的WHERE条件逻辑中 AND、OR、LIKE、IN、=、!= 等算子和组合拼接,最后在内部将其转换为各个引擎定制的DSL发起查询请求。
{
“page”: 1,
“size”: 20,
“where”: “entity_type = 1 and sec_type = 3 and properties.tabName like ‘%r_ai.ods.recindexing.archive.test%’”
}
由于实际场景中有大量的关联查询需求,而我们的数据存储模型是类似于雪花模型的结构,为了降低多次查询的复杂性,我们用特殊的字段设计和查询语法支持了一次查询时的额外多层关联查询。
{
“page”: 1,
“size”: 500,
“where”: “entity_type = 7”,
“extraProperties”: {
“t1”: “:.pgUrn.text_pageName", “t2”: “7:.pgUrn.text
p
ageName”,“t2”:“7:.pgUrn.text_userName”,
“t3”: “7:.pgUrn", “t4”: “*:.pgUrn”,“t4”:"∗:.pgUrn.bizCtime”,
“t5”: “:.dsUrn.sql”, “t6”: “guanyuanCard:.dsUrn.sql”,“t6”:"guanyuanCard:.dsUrn.datasetStatus”
}
}
目前,通用元数据查询已经全面应用在数据地图、影响分析、指标取数服务等业务应用场景上面,存量的定制查询也在逐步迁移。
血缘建设
数据血缘是元数据基建中非常比较重点的方向,甚至可以说,元数据建设的收益中,30%~50%是血缘建设。描述好数据的来龙去脉,能充分解释一份数据从哪里来到哪里去,是后续开展数据运维、数据治理工作的关键。
我们将血缘建设主要分成三个主攻方向:提升覆盖、细化粒度、保障准确性。其中第三点保障准确性目前相对较难,我们也还处于探索阶段,所以重点围绕前两个方向来讲。
提升覆盖
提升元数据的覆盖需要两个前提,一是数据生产或使用的链路收敛、系统数据可采集;二是参与数据生产使用的系统,需要有统一的数据定义。
链路收敛意味着分母数量确定,提升覆盖不会变成一个无法预期、无限投入的工作。比如在B站内部,参与数据生产的系统,统一到了平台调度平台、流计算平台、数据集成平台、埋点平台几个有限系统中,我们根据这些系统中的要素去定制血缘解析和采集策略,将数据进行打通,即可覆盖离线、实时、出入仓等关键步骤的血缘,但往往还会存在一些由业务定制的野生调度系统,野生运行脚本等跑数情况,这些场景一般伴随着缺少归属人,生产模式杂乱,缺失生命周期等问题,正常不应该纳入到血缘链路中,最好尽快的收口治理掉。
统一的数据定义,可以参考上面统一资源表达式URN,需要推动各个系统达成共识。尤其对于涉及出入仓的系统,对数据源的统一管理,全面接入是对出入仓数据统一定义的关键点。
目前我们在血缘的覆盖度建设上面比较完善,目前已经较为完整的覆盖了离线链路、实时链路、出入仓表、数据报表等等。
细化粒度
血缘的粒度由大至小分别是 表级 → 字段级 (分区级) → 行级,血缘粒度越小,进行数据链路上下游定位的精度越高,但采集解析存储的难度越大。
表级血缘是非常基础的能力,一般使用类似Antlr等开源的SQL解析器进行ETLSQL静态解析,结果也比较精准。一般的离线调度、实时计算平台都会自建这类scan能力,难点是对于非SQL的ETL任务,比如MRJar、SparkJar类型的任务,解析原生代码的难度很大而且结果很大概率会不准,一般会尽量收敛在重要的链路使用,或者扩充功能,由用户手动维护这类任务的输入输出表。对于出入仓的表血缘,一般则是功能化选择入仓表、出仓表,可以直接获得血缘。
字段级血缘随着平台建设的深入和治理工作的开展,越来越趋于重要,因为从表粒度定位上下游的精度太粗,比如在字段变更影响分析时,通过表血缘会筛出很多实际无依赖表,需要再耗费很多人力去看代码筛选。实现字段级血缘,有三种可选方案:a. 事前+静态 b. 事前+动态 c. 事后+动态。
事前+静态同解析表级血缘的思路一样,但是解析的准确性很差,处理不了类似于select *等不明确写明字段的情况。事前+动态是在任务注册时,通过调用Hive引擎的动态解析能力,产出LineageLog日志,用于字段级血缘解析,这种方法是可行的,优点是获取血缘的时效性比较高,缺点是需要感知生产任务的注册变更主动发起解析,如果生产系统不够收敛,实现的成本较大。事后+动态是在任务实际执行时,经过Hive引擎的动态解析过程后,自动抛出LineageLog,进行字段级血缘解析,这种方案也是可行的,优缺点和事前+动态相反,时效性较低,但是只需要被动采集日志,不用感知任务变化。我们采用的是方案3,当然,在实际情况中,我们还需要面临Hive之外的引擎适配,比如Spark、Presto执行,但思路相类似,都需要引擎侧的支持。
行级血缘只在非常特殊的场景存在需求,比如埋点链路追踪,可以通过其他定制化手段加以解决,统一的行级血缘暂时无法实现。
目前我们的血缘粒度支持到字段级,但是字段级还存在不少的限制,比如某些系统生产的数据不支持字段级,报表血缘不支持字段级等等,此外,一直缺乏对字段级血缘的准确性评估的有效手段,目前只能借助于类似于影响分析、字段属性继承等业务场景的用户反馈。
现状总结&当前规模
目前元数据基建已经建设成熟,拥有基于统一模型的元数据采集、存储、查询、监控、运维的一站式能力。目前建立10+元数据采集上报方,接入实体类型16种,关系类型10种,其中Hive正式表数量6W+,各类任务数量11W+。
表级血缘覆盖从数据入仓到出仓全链路,打通离线表与实时表血缘,表级血缘覆盖平台正规调度任务产出的所有表字段。
元数据通用查询每日支撑各类业务查询PV2.5W次,支撑上层 数据地图、影响分析、血缘地图、取数服务、基线分析 等重要平台应用。
元数据应用-数据地图
找数
找数是数据运营中的关键环节,也是数据地图要解决的核心问题。我们将地图模块分为 基础搜索、分类查询、热度推荐 三部分。
基础搜索重点解决用户主动找数的场景,其中涉及数据模型的搜索召回策略、排序策略。我们将表名、描述信息、责任人、字段、标签等字段作为模型召回字段,通过关键词匹配度、 模型热度、模型质量、模型推荐标 以及适当的权重分配,进行排序控制,最终展现用户需要的搜索结果。
分类查询、热度推荐 重点解决用户被动找数的场景,首先需要对业务域、数据域和数据过程进行合理划分,构建完善可读的数据目录,用户通过对目录信息的浏览,可以定位到具体业务表。热度推荐则是通过模型使用热度,按照部门划分进行排序,推荐出同部门用户高频使用、近期新增的表。
除了Hive表之外,数据地图还提供了 实时Topic、clickhouse表、Bi报表的搜索查询,目前地图搜索日查询PV 4000+。
理解数
为了用户找数后,理解模型数据的内容,极大丰富了表详情页的功能,重点围绕构建表的模型画像、数据画像,这里面非常依赖元数据的基建能力进行采集和质量校验。
模型画像,我们从以下几个方面对表的信息进行了刻画:
基础元数据(表名、字段、分区、路径、格式等)
业务元数据(归属信息、安全等级、业务线、模型信息、生命周期等)
生产元数据(产出任务、基线)4. 质量元数据(DQC任务)
衍生元数据(使用说明、自定义标签、评分)
血缘元信息(表血缘)
变更元信息(变更记录)
成本元信息(表存储占用,分区存储占用,冷存周期,压缩格式)
使用元信息(使用热度)
数据画像,目前支持的功能主要是样例数据和数据探查,用以展示表数据的内容,并具备一些基础统计分析能力。
图片
元数据应用-血缘地图
血缘地图需要满足用户探索数据血缘的需求,是血缘元数据最直接的产品化呈现,在产品设计实现的过程中,我们遇到了非常多的问题,也走了一些弯路,才探索出一套可用的形态。目前最终呈现的数据地图,支持动态配置不同类型数据的展示信息,支持点的动态条件过滤、高亮。
目前血缘地图中涉及的主要实体类型12种,关系构建实体类型4种,日均使用PV 500+。
图片
元数据应用-影响分析
影响分析主要使用场景有两个:
上游数据变更或异常,判断定位下游影响
下游数据异常,进行问题溯源
所以在这个产品定位下,影响分析的核心能力就是支持血缘深层遍历,数据汇总统计,我们在此功能上首次支持了字段血缘。在这个场景中,我们依然要面对数据类型多的问题,初此之外,还要面对深层遍历时长耗时的交互处理,超大数据量(过百层,百万级实体)结果处理,已经超大数据对服务资源占用的影响。针对这几种情况,我们的处理方式是:
异步执行,同步交互(95%可以10s内返回)
利用HugeGraph的图深层遍历能力,隔离服务集群
数据汇总处理业务,隔离到单独服务
相同查询条件结果天级缓存
图片
图片
未来规划
元数据质量保障,目前已经落地一套保障机制,但目前接入保障的场景还比较少,需要长期推广和推动存量上报迁移,形成质量评估的体系。
元数据字典,随着越来越多元数据类型的接入,沉淀了各类元数据的业务属性,要形成基于通用查询的完全自助查询,需要通过建立元数据字典,解决元数据模型和字段业务含义的理解问题。
数据运营体系,随着功能的拓展,平台功能已经覆盖到用户方方面面的需求。但平台建设,除了建工具之外,还有需要建流程,建机制。目前在找数用数场景中,最核心的痛点就是模型质量不高,模型分类不准不全,下游使用存在数据口径问题,数据质量问题、数据使用问题。我们需要建立数据运营机制,从数据供给侧建立成本指标和产出指标,数据消费侧打通数据使用链路血缘,建立收益指标,利用地图的能力保障数据生产消费两端的信息畅通。
数据治理,在数据平台的建设中,由于各种历史原因,普遍存在大量重复建设,不规范的行为动作,导致数据成本,人力成本的多余消耗。随着降本增效成为业务重心,我们需要从工具层面开展数据治理建设,利用已经完善的元数据基建能力,规模化治理流程,扩大治理范围,提升治理效率。