有赞数据仓库背景
- 业务系统使用 mysql 数据库
- 数据仓库基于 Hive 构建
- 业务快速变化,员工数量持续增加
第一版:手工维护的表格
在有赞大数据平台发展初期,业务量不大,开发者对业务完全熟悉,从 ETL 到统计分析都可以轻松搞定,当时没有想过要做一个元数据系统。
随着公司规模扩大,开始有专职的数据分析师,作为大数据平台的新用户,希望能够记录和查看核心表的信息。最简单的方法就是去业务数据库里查看注释,但是一方面业务数据库的注释不全或不准,另一方面分析师的视角和开发者不同,需要从不同角度去描述表或字段,比如完整的枚举值含义、业务统计口径等。
于是有了第一版的数据字典,手工维护一系列核心的业务表和统计报表,记录了字段含义、统计口径的业务描述和 sql 语句等,用一个 web 界面展示。
第二版:自动采集的数据字典系统
第一版的数据字典,其实就是一堆表格,像个 wiki,大家都可以上来编辑。当公司业务快速发展,靠人工维护这些表格已经力不从心。新增加了几个业务线,很多新增的表格无法查到,旧业务线也不断增加新表、新字段,手工维护的表格里的信息会不准。此时最强烈的需求是希望能获取到最新的表和字段信息。
我们尽量使用了拉取的方式,而不是订阅消息,开发成本更低:
- 对于 mysql,定时从 DDL 管理系统查询最近有变更的表及其表结构;
- 对于 Hive 每半分钟扫描查询一次 Metastore,获取最近 DDL 的表名,再通过 Hive JDBC 做表结构同步
- 定期全量从 mysql、Hive 里同步所有的表
技术方案落地的同时,我们还推行了 mysql DDL 的规范,表和字段必须加上注释,一个简单的规范可以省去很多后续的维护成本。
我们还遇到了因为业务变更,引起的报表不准确。在同步表和字段的基础上,我们又做了 DDL 变更的告警,这是首次做到通过元数据发现问题。
第三版:元数据驱动数据仓库
公司业务继续快速发展,又多了几个新业务线,有更多部门的用户在使用数据仓库。作为数据仓库的管理者,会遇到更多的问题。数据仓库的管理者希望能方便的看到系统或各个表的状态,数据仓库的用户希望能查到更多表的业务信息。而此时数据仓库还是一个黑盒,双方需求都不能满足,日常沟通、问题验证的成本很高,需要更多元数据的采集和展示。
参考 Kimball 的数据仓库理论,把元数据分为这三类:
- 技术元数据,如表结构、文件路径/格式;
- 业务元数据,如责任人、归属的业务、血缘关系;
- 过程元数据,如表每天的行数、大小、更新时间。
尽量把这三类数据都自动采集或计算获取,然后在 web 页面上展示或对外提供接口。这是当前版本的元数据系统,下面列举我们实现的核心功能。
血缘关系
“表”是元数据系统的后台逻辑核心,数据仓库是构建在 Hive 之上,而 Hive 的原始数据往往来自于生产系统,也可能会把计算结果导出到外部存储,所以我们认为 Hive 表、mysql 表、hbase 表、BI 报表都是“表”,这些“表”间关系是一个 DAG,也就是血缘关系。
表间的关系全部体现在 SQL 和 ETL 任务里。
- SQL:早期我们把 SQL 提交到 Hive 服务端做 explain,得到 import 和 output。为了支持字段级血缘分析,以及支持 Presto SQL 方言,我们对 Hive 提供的抽象语法树解析代码做了修改。
- ETL:我们的 ETL 工具基于开源的 datax,数据源/目标包括了 Hive、mysql、Elasticsearch、HBase、kafka 等,每天收集这些 ETL 任务,并记录好源头和目标即可。
- MapReduce/Spark 脚本:有不少这类离线计算,大部分都会用到 Hive 表,但是很难从 python/scala 这样的高级编程语言代码里解析出表名,此处做人工介入,创建或修改任务时,需要记录这个任务读写了哪张表。
血缘关系案例
有了血缘关系,基于开源 jsmind 库做了展示,可以让用户清楚看到一张表的上下游,更方便地查找表。基于血缘关系可以做很多事情,例如:
- 结合表的更新时间,还可以找到调度 DAG 的关键路径,协助定位性能瓶颈;
- 当表出现变更时,可以通知下游责任人,以及自动对下游任务做 SQL 的静态检查;
- 辅助生命周期管理,找到没有被使用的表/字段;
- 辅助维护字段的一致性,如注释、校验规则复用。
Hive 表行数/更新时间
我们记录了每个离线计算任务对应的 Hive 表,当某个任务执行结束,会立即做表行数/更新时间的采集。
- 文件大小/更新时间:metastore 有记录但是不准。我们完全基于 hdfs,读取文件的属性作为 Hive 表的大小与更新时间。再根据任务的启动时间,就可以得到计算出表所对应任务的执行时长。
- 行数:非常重要且明智的选择,使用 Presto 对全表或分区做 count 计算,这个 SQL 的执行效率是惊人的。对 orc 文件,简单的 count 计算,Presto 并不会整个文件扫描,而仅仅读取 orc 文件的 index data 部分就可以得到行数。在实际运行中,几亿行的表都可以秒级得到行数。为了采集表的行数,我们下定决定把所有 Hive 表的存储格式改成 orc。
表行数、文件大小、更新时间、运行时长的数据采集一段时间后,就可以形成曲线图,数据仓库管理员和开发者都可以从这个图中发现规律或数据质量问题,甚至业务系统开发者也会通过这个曲线图来查看业务量变化趋势。在行数采集的功能基础之上,我们还做了数据质量校验、数值分布探查等功能。把多个关键表的产出时间绘制到一张图里,可以清楚的看出数据仓库的稳定情况。
live表历史趋势案例
计算资源管理
前面提到离线任务结束,会做表大小/行数采集,同样的道理,也可以到 yarn 上采集相关任务使用的资源情况。如果是 MapReduce 任务,可以采集到完整的CPU、内存、磁盘 IO 使用情况,而 spark 任务则只能采集到 CPU 使用情况。把这些资源使用情况绘制成曲线图,很容易发现 SQL 的变更或异常情况。也可以把消耗的资源换算成硬件成本,找到最消耗资源的表或业务,督促开发者优化。
计算资源趋势图案例
数据同步规则描述
有赞的业务发展快,同时也意味着业务数据模型变化快,经常会发生业务表增减字段、拆表、迁库等动作,基于元数据的数据同步配置,很大程度避免了因为配置不一致导致的错误。在 mysql 侧,基于表结构的准实时同步,元数据维护了字段-表-库的映射关系,甚至记录了表迁移前后的地址映射关系,根据最新的状态动态生成数据同步配置。在 Hive 侧,记录了表是否分区、分区键、文件格式、hdfs 路径等信息,用于生成读写 Hive 或 HDFS 的配置。
业务域管理
以前我们会维护一张表格,记录数据仓库每个业务的对接人,方便用户找到我们。后来我们把这个表格扩展成了业务域管理,除了记录业务与人的关系,还可以看出每张表属于哪个业务。
受到了 apache ranger 的启发,我们使用表名匹配的方式来动态的计算表所归属的业务域,只需配置几十个业务域规则,而不用对每张表分别配置。而与 ranger 采样相同匹配规则,使得我们做到了表、用户、业务多个业务视角任意切换,提升了元数据系统的业务扩展性,开发效率大大提升。
业务域扩展的权限管理
前面提到业务域是使用与 apache ranger 相同的表名匹配的管理方式,这样就天然把业务域扩展到了权限管理上。先给相关业务域的管理员或开发者配置相应权限,其余权限均需要申请。元数据系统并不存储这些权限信息,而完全依靠于 ranger,这样避免出现不一致。
用户执行 SQL 时,利用 Hiveserver 自动在 ranger 做表级的鉴权;当用户申请权限并审批通过,会相应的在 ranger 上增加一条记录。
对于敏感字段,首先我们会通过定期的全库采样并分析的方式,判断字段是否是诸如手机号、邮箱、证件号等敏感字段并标记上密级,同时在 ranger 上增加 masking 配置,默认只显示其中的几位,例如证件号只显示最后 4 位。