无规则采集器列表算法(数据资产治理(详情见:数据、赞之治理)的前提要有数据)
优采云 发布时间: 2021-12-22 13:13无规则采集器列表算法(数据资产治理(详情见:数据、赞之治理)的前提要有数据)
数据资产治理(详见:数据资产、赞智治理)需要数据。它要求数据类型齐全,数量大,并尽可能覆盖数据流通的方方面面。元数据 采集 变得尤为重要。是数据资产治理的核心基础。
在早期的采集系统中,我们主要关注数据仓库,通过“API直连方式”采集Hive/Mysql表元数据。随着业务的快速发展,对数据运营和成本管理的需求越来越强烈。元数据需要覆盖整个数据链路,包括离线计算平台、实时计算平台、内部工具、任务元数据等。在采集元数据的过程中,我们遇到了以下难点:本文主要介绍一些我们从元数据、提取、采集、监控告警等方面做的事情。二、元数据2.1 什么是元数据?什么是元数据?元数据是“用于描述数据的数据”。例如:
照片信息<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >文件名:IMG_20201217_114115<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >时间:2020年12月17号 11:30:01<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >分辨率:4608X2592<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >文件大小:2.69MB<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >相机制造商:OnePlus<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >相机型号:ONEPLUS A5000<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >闪光灯:未使用闪光灯<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >焦距:4.10mm<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >白平衡:自动<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >光圈:f/1.7<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >曝光时间:1/50<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >ISO:1250
这些是数码照片的元数据,用于描述图片。在资产管理平台,我们采集Hive组件的元数据包括:表名、字段列表、负责人、任务调度信息等,采集全链路数据(各种元数据)可以帮助数据平台回答:我们有什么数据?有多少人在使用它?什么是数据存储?如何找到这些数据?什么是数据流?分析问题的根源,结合血缘关系分析影响。2.2 采集 下图是什么元数据,是一个数据流图。我们主要采集各个平台组件:
截至目前,采集所到的平台组件已经覆盖了整个数据链路。涵盖10种数据+,基础元数据量10w+。主要包括: 三、 Metadata Extraction 如何从众多平台组件中提取元数据?大致有这几个方面:计算任务通过分析任务的输入/输出依赖配置来获取血缘关系。SQL类任务使用“Sql Parser”(ANTLR4系统实现的sql重写工具)工具解析SQL脚本,获取表/字段级血缘关系。3.1 离线平台主要是采集Hive/RDS表的元数据。
Hive组件的元数据存储在Metastore中,通过JDBC访问Mysql获取数据库表的元数据。根据Hive表信息组装HDFS地址,通过FileSystem API获取文件状态、文件编号、文件大小、数据更新时间等趋势数据。RDS平台提供Mysql服务的管理,通过平台提供的服务接口获取表元数据、趋势数据、访问状态等信息。3.2 实时平台主要是Flume/Hbase/Kafka等组件的元数据。例如:我们访问放置在KP平台的工单数据,获取topic的基本元数据信息,定期消费topic获取样本数据,解析字段列表。平台本身提供集群状态和业务监控指标,通过平台服务获取集群资源的使用情况。3.3 内部工具主要是BI报表系统(一个BI报表查询的Hive表和Mysql表的关系),指标库(Hive表和指标关联的字段的关系),和 OneService 服务(接口访问哪些库表)关系数据)血缘关系数据。这些内部系统在产品的不断迭代中积累了大量的元数据。在不考虑元数据的时效性的情况下,我们一般都是将这些系统的数据同步到Hive数据库中,然后离线处理后获取元数据。3. 4 任务元数据元数据任务主要是DP离线任务、Flink计算服务和Flume任务。这些计算任务都放在磁盘上,通过Binlog同步或离线同步获取任务列表,获取任务的元数据。四、Data采集 元数据提取后,我们可以得到数据链中各个平台组件的元数据。数据采集是指将这些元数据存储在数据资产管理系统的数据库中。4.1 采集Methods采集 数据主要有3种方法。下表列出了三种方法的优缺点:通过Binlog同步或离线同步获取任务列表,获取任务的元数据。四、Data采集 元数据提取后,我们可以得到数据链中各个平台组件的元数据。数据采集是指将这些元数据存储在数据资产管理系统的数据库中。4.1 采集Methods采集 数据主要有3种方法。下表列出了三种方法的优缺点:通过Binlog同步或离线同步获取任务列表,获取任务的元数据。四、Data采集 元数据提取后,我们可以得到数据链中各个平台组件的元数据。数据采集是指将这些元数据存储在数据资产管理系统的数据库中。4.1 采集Methods采集 数据主要有3种方法。下表列出了三种方法的优缺点:@采集Methods采集 数据主要有3种方法。下表列出了三种方法的优缺点:@采集Methods采集 数据主要有3种方法。下表列出了三种方法的优缺点:
一般情况下,我们推荐业务方使用采集SDK。主动上报元数据,访问时只需要关注上报数据格式和SDK初始化,即可快速完成上报。4.2 采集SDK设计采集SDK支持基础元数据、趋势数据、血缘关系数据的上报,包括客户端SDK和采集服务器两部分。客户端SDK主要实现通用报表模型的定义和报表功能,采集服务器主要实现不同的适配器,完成数据的统一存储。4.2.1 架构
采集SDK 客户端定义了基本元数据(MetaSchema)、趋势数据(TrendSchema)、血缘关系数据(LineageSchema)的通用模型,并支持扩展新的报表模型(XXXSchema)。ReportService实现了向Kafka推送数据的功能。采集服务端数据认证服务端消费Kafka,获取数据后,验证每条记录的签名(获取记录中的appId、appName、token信息,重新生成token并比较值)。统一仓储服务定义了统一的数据仓储模型,包括表基础元数据、趋势数据、血缘关系数据、趋势数据,实现不同数据类型的仓储服务。数据适配器 Bridge 获取 Kafka 的数据,将其转换为“统一存储模型” 根据不同的数据类型,触发“统一存储服务”完成数据写入。4.2.2 通用模型采集 平台组件很多。我们参考Hive“表模型”的定义,抽象出一套通用的数据上报模型,保证数据上报和数据存储的可扩展性。通用血缘模型主要包括血缘模型定义和任务血缘模型定义,支持用户分别上报血缘关系和任务血缘关系。该模型定义如下:我们参考Hive“表模型”的定义,抽象出一套通用的数据上报模型,保证数据上报和数据存储的可扩展性。通用血缘模型主要包括血缘模型定义和任务血缘模型定义,支持用户分别上报血缘关系和任务血缘关系。该模型定义如下:我们参考Hive“表模型”的定义,抽象出一套通用的数据上报模型,保证数据上报和数据存储的可扩展性。通用血缘模型主要包括血缘模型定义和任务血缘模型定义,支持用户分别上报血缘关系和任务血缘关系。该模型定义如下:
/**<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > * 表血缘模型定义<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > */<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >@Data<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >public class TableLineageSchema {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > **<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > * 当前节点<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > */<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > private T current;<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > **<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > * 父节点<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > */<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > private List parents;<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > **<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > * 子节点<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > */<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > private List childs;<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > /**<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > * 表级别血缘扩展信息,json对象,kv结构<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > */<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > private String extParam;<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" ><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >
/**<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > * 表任务血缘定义<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > *<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > */<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >@Data<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >public class JobLineageSchema {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > /**<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > * 任务节点对象<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > */<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > private Job task;<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > /**<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > * 输入对象列表<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > */<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > private List inputs;<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > /**<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > * 输出对象列表<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > */<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > private List outputs;<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > /**<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > * 任务级别血缘扩展信息,json对象,kv结构<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > */<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" > private String extParam;<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" ><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >
每个模型定义都有一个扩展字段(传统的 json 格式)。不在定义中的指标可以放在扩展字段中。数据上报后,也会存储在元数据表的扩展字段中。访问新的类型,索引定义大不相同,元数据报告是通过扩展新的数据模型定义来完成的。4.2.3 访问、验证、限流如何保证用户上报数据的安全?我们设计了一组签名:访问方Id(appId)、访问名称(appName)、访问标识(token)。管理员填写基本接入方信息,生成随机的appId和token信息。业务方初始化采集SDK时,指定签名信息,并且每上报的数据都会带有签名。在采集服务器上,每一条数据都会经过签名和认证,保证了数据的安全。采集SDK 会对上报的每条数据执行通用规则来检查数据的有效性,例如表名不为空、负责人的有效性、表的大小、趋势数据不能为负数等。如果检测到非法数据,将被过滤掉并触发报警通知。在采集SDK服务器上,每隔一定时间(每两秒)消费一批Kafka数据。支持设置消费数据的时间间隔和拉取的片数。下游入站压力不会因上报数据流量高峰而发生变化。大,起到了限流的作用。4.3 Trigger采集 我们支持多种采集元数据方法。如何触发数据的采集?总体思路是:基于Apollo配置系统(见:Apollo在实践中的好评)和Linux系统的Crontab功能,实现任务调度。
数据采集任务在Apollo上配置。配置改变后,Apollo会发布,配置信息会实时同步到在线节点的Crontab文件中。4.3.1 增量任务,准实时支持获取组件最近变化的元数据,配置增量任务,提高元数据的实时性采集。比如增量采集Hive表元数据,每1分钟查询一次metastore,获取最近更改的元数据列表,并更新元数据。4.3.2 全量任务,底部增量采集 可能存在数据丢失的场景,全量采集每1天或多天一次作为底部计划保护元数据的完整性。4.< @3.3 采集SDK,实时上报采集SDK支持实时和全量上报模式。一般要求接入方数据发生变化后实时上报,同时不定期全量上报一次。4.4 数据存储,更新数据后采集,必须考虑如何存储,以及元数据发生变化时如何同步更新。我们对来自采集的元数据进行归类统一,抽象出“表模型”,分类存储。4.4.1 数据存储我们评估了每个组件的元数据量(共10w+),估计了数据可能的使用场景,最终选择了Mysql存储。为了满足用户个性化的查询需求,构建了Es宽表。
元数据中的表不是孤立存在的。一般有关联任务(离线任务、实时任务)输出表,表和任务之间的流向关系也会在数据图中显示。那么如何在众多平台组件中唯一区分一个表呢?我们通过表所在的集群名称、项目名称、表类型(它来自哪个平台组件)和表名称的组合来唯一区分。对数据进行分类存储,最终形成:基础元数据表、趋势数据表、任务元数据表、血缘关系数据表。4.4.2 数据更新元数据表离线,如何同步更新?五、监测预警已完成数据采集,都完成了吗?答案是否定的。采集在这个过程中,数据类型很多,删除方式多种多样,删除链接长度。任何环节的任何问题都会导致结果不准确。我们通过以下方式来保证采集服务的稳定性。5.1 采集链路监控告警5.1.1 接口监控 我们将系统的所有服务接口分为三个层次:核心、重要、通用、支持注解。接口和负责人的方式相同,异常触发不同级别的报警通知。核心业务异常直接触发电话报警,重要或一般业务异常触发电子邮件报警。系统会存储接口请求和执行状态并删除,并每天向接口服务负责人发送服务日报。通过将采集服务的元数据标记为核心和重要服务,“API直连方式”的接口异常感知。如下图,是服务接口的告警通知:
[Warning][prod][data-dict] - 数据资产平台告警<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >你负责的[元信息采集]模块(backup为XXX)出现[重要]等级问题, 方法名:[com.youzan.bigdata.crystal.controller.HiveMetaController.getHiveDb], 异常信息:null<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >host:XXXXXX<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >处理地址:https://XXXX<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >
如下图,是服务接口的每日告警报告:
[Warning][prod][data-dict] - 数据资产平台告警<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >[shunfengche]今日问题汇总<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >请及时收敛今日问题,总问题数 1 个,出现 2 次<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >【核心】问题 0 个:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >【重要】问题 0 个:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >【一般】问题 1 个:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >[数据采集]com.youzan.bigdata.crystal.controller.HiveMetaController.getHiveDb 今日出现 2 次, 已存在 5 天, 历史出现 8 次<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >host:XXXXXX<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >处理地址:https://XXXX<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >
5.1.2 采集 进程监控是针对每个元数据采集服务的,当采集进程异常时会发出告警通知。如下图,是采集过程中异常触发的告警:
[Warning][prod][data-dict] - 数据资产平台告警<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >你负责的[元信息采集]模块(backup为XXX)出现[一般]等级问题, 方法名:[com.youzan.bigdata.crystal.asyncworker.work.AsyncAllRdsDDLWorker.run], 异常信息:/n<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >### Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLQueryInterruptedException: Query execution was interrupted<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" >
5.1.3 Kafka消息积压告警消耗kafka数据,通过kp平台配置消息积压告警,实现对采集SDK服务的异常感知。5.2 结果数据比对主要用于事后监测预警,定期检查采集的元数据量是否有异常波动。针对不同类型的元数据,通过将当天采集的数量与过去7天的历史平均数量进行比较,设置异常波动的告警阈值,超过阈值时触发告警通知. 针对采集的元数据结果表,配置一些数据质量检测规则,定期执行异常规则,发现问题数据时触发告警通知。这保证了对结果数据的异常感知。比如定义的数据质量规则:5.3个项目迭代机制,采集问题收敛,通过事前、事中、事后的监测预警机制,可以检测感知采集 时间异常。对于异常问题,我们一般以项目迭代的方式发起jira,组织相关人员进行审核。追根溯源,讨论改进方案,产生行动,关注并持续收敛问题。六、总结与展望6.1 总结 我们定义了一套通用的数据采集和存储模型,支持访问不同数据类型的元数据,支持多种访问方式,采集@ >SDK提高访问效率和数据时效性。
如果将数据资产治理比作高层建筑的建设,那么不同构件的元数据是原材料,数据采集是基础。只有夯实了基础,数据治理的建设才能越来越稳固。6.2 期待数据采集,我们也遇到了很多问题。在后续的工作中,我们需要不断的优化和功能迭代,包括但不限于:
最后,有赞数据中心,长期招聘基础组件、平台研发、数据仓库、数据产品、算法等人才,欢迎加入,一起享受~简历投递邮箱:。
延伸阅读:Flink 的滑动窗口优化了点赞和埋点的做法。点赞和埋点的质量保证是基于用户购买意愿的分类时间加权计算
有赞推荐系统关键技术
有赞数据中心建设实践
数据资产,赞治治理
SparkSQL在有赞大数据实践中(二)HBase Bulkload实践讨论)