数据中台学习笔记-数据质量,成本优化,数据服务
概述
上一篇简单介绍了元数据管理,指标管理,数据模型,现在我给大家介绍一下数据质量,成本优化,数据服务的相关的知识。
数据质量
从模型设计层面,逐步将分散、杂乱、烟囱式的小数仓整合成了可复用、可共享的数据中台,数据研发效能提升了一倍。那是不是交付数据足够快,使用数据的人就满意了?光快还不够,还要保证质量。在数据中台中常常会遇到一下类似的问题:
数据部门晚于业务方发现数据异常,被投诉后才发现问题。
出现问题后,数据部门无法快速定位到数据异常的根源,排查用了较长的时间。
故障出现在数据加工链路的上游顶端,出现问题没有第一时间报警处理,导致问题修复时,所有下游链路上的任务都要运行,修复时间成本非常高。
这些问题最终导致了数据长时间不可用。那如何解决这些问题,确保数据高质量的交付呢? 首先,你要了解产生这些问题的根源,毕竟认识问题才能解决问题。
数据质量问题的根源
我们对事件的原因进行了归纳,主要有下面几类。这里多说一句,如果你想改进数据质量,不妨也对过去踩过的坑做一次复盘,归一下类,看看问题都出在哪里,然后制定针对性的改进计划。
业务源系统变更
数据中台的数据来源于业务系统,而源系统变更一般会引发 3 类异常情况,
首先是源系统数据库表结构变更。例如业务系统新版本发布上线,对数据库进行了表结构变更,增加了一个字段,同时对部分字段的类型、枚举值进行了调整。这种表结构变更没有通知到数据团队,导致数据同步任务或者数据清洗任务异常,进而影响了下游数据产出。
第二个是源系统环境变更。我经常在大促期间见到这种情况,其中的典型是前端用户行为埋点日志量暴增,系统管理员紧急对服务器进行扩容,上线了 5 台新的服务器,但是没有配置这 5 台服务器的日志同步任务,结果导致数据侧少了这 5 台服务器的数据,最终影响了数据计算结果的准确性。
最后一个是源系统日志数据格式异常。这种情况通常出现在前后端埋点日志中。业务系统发布上线引入埋点 BUG,导致 IP 格式出现了不符合约定的格式(比如,我们一般约定的 IP 格式是 166.111.4.129,结果出现了 166.111.4.null),最终也会导致计算结果错误。
数据开发任务变更
这种情况在数据质量事件中占到了 60% 以上,而它大多数是由于数据开发的纰漏引发的,来看几个你比较熟悉的例子:
任务发布上线,代码中引用的测试库没有修改为线上库,结果污染了线上数据;
任务发布上线,代码中使用了固定分区,没有切换为“${azkaban.flow.1.days.ago}”,导致数据异常;
前面例子中,数据格式处理错误,代码忽略了异常,导致数据错误;
任务配置异常,它通常表现在任务没有配置依赖,前一个任务没有运行完,后一个任务就开始运行,输入数据不完整,导致下游数据产出错误。
物理资源不足
在多租户下,Hadoop 生态的大数据任务(MR,Hive,Spark)一般运行在 yarn 管理的多个队列上(调度器为 CapacityScheduluer),每个队列都是分配了一定大小的计算资源(CPU、内存)。
基础设施不稳定
从数量上来看,这类异常不算多,但影响却是全局性的。我们曾经在大促期间,碰到了一个Hadoop 2.7 NameNode 的 BUG,造成 HDFS 整个服务都停止读写,最终通过临时补丁的方式才修复。总的来说,出现问题并不可怕,可怕的是,我们没有及时发现问题,尽快恢复服务,举一反三地通过流程和技术手段,降低问题出现的概率。所以接下来我们就来看一看,如何提高数据质量?
如何提高数据质量?
我认为,要想提升数据质量,最重要的就是“早发现,早恢复”:
早发现,是要能够先于数据使用方发现数据的问题,尽可能在出现问题的源头发现问题,这样就为“早恢复”争取到了大量的时间。
早恢复,就是要缩短故障恢复的时间,降低故障对数据产出的影响。
那具体如何做到这两个早呢?我总结了一套数据质量建设的方法,包括这样几个内容。
添加稽核校验任务
在数据加工任务中,对产出表按照业务规则,设计一些校验逻辑,确保数据的完整性、一致性和准确性,这是提升数据质量最行之有效的方法。通常建议你在数据产出任务运行结束后,启动稽核校验任务对数据结果进行扫描计算,判断是否符合规则预期。如果不符合,就根据提前设定的强弱规则,触发不同的处理流程。如果是强规则,就立即终止任务加工链路,后续的任务不会执行,并且立即发出电话报警,甚至我们要求,关键任务还要开启循环电话报警,直到故障被认领;如果是弱规则,任务会继续执行。但是存在风险,这些风险会通过邮件或者短信的方式,通知到数据开发,由人来进一步判断风险严重程度。
那具体要加哪些稽核规则呢?完整性规则。主要目的是确保数据记录是完整的,不丢失。常见的稽核规则有表数据量的绝对值监控和波动率的监控(比如表波动超过 20%,就认为是异常)。还有主键唯一性的监控,它是判断数据是否有重复记录的监控规则,比较基础。除了表级别的监控,还有字段级别的监控(比如字段为 0、为 NULL 的记录)。一致性规则。主要解决相关数据在不同模型中一致性的问题。商品购买率是通过商品购买用户数除以商品访问 uv 计算而来的,如果在不同的模型中,商品购买用户数是 1W、商品访问 uv10W,商品购买率 20%,那这三个指标就存在不一致。准确性规则。主要解决数据记录正确性的问题。常见的稽核规则有,一个商品只能归属在一个类目,数据格式是不是正确的 IP 格式,订单的下单日期是还没有发生的日期等等。
它们是强规则还是弱规则,取决于业务对上述异常的容忍度(比如涉及到交易、支付跟钱相关的,一般都会设置为强规则,对于一些偏行为分析的,一般都是弱规则)。
建立全链路监控
数据中台的模型设计是分层的,确保中间结果可以被多个模型复用。不过这会导致数据加工的链路变长,加工链路的依赖关系会非常复杂,最终当下游表上的某个指标出现问题,排查定位问题的时间都会比较长。所以,我们有必要基于数据血缘关系,建立全链路数据质量监控。
从这个图中你可以看到,业务系统的源数据库表是起点,经过数据中台的数据加工链路,产出指标“黑卡会员购买用户数”,数据应用是链路的终点。对链路中每个表增加稽核校验规则之后,当其中任何一个节点产出的数据出现异常时,你能够第一时间发现,并立即修复,做到早发现、早修复。另外,即使是使用方反馈经营分析上的黑卡会员购买用户数,相较于昨天数据大幅下降超过 30%,你也可以快速判定整个指标加工链路上节点是否运行正常,产出任务是否有更新,提高了问题排查速度。
通过智能预警,确保任务按时产出
在数据质量问题中,我提到会存在物理资源不足,导致任务产出延迟的情况。在网易,所有数据中台产出的指标要求 6 点前产出。为了实现这个目标,我们需要对指标加工链路中的每个任务的产出时间进行监控,基于任务的运行时间和数据血缘,对下游指标产出时间进行实时预测,一旦发现指标无法按时产出,则立即报警,数据开发可以终止一些低优先级的任务,确保核心任务按时产出。
通过应用的重要性区分数据等级,加快恢复速度
稽核校验会消耗大量的资源,所以只有核心任务才需要。核心任务的定义是核心应用(使用范围广、使用者管理级别高)数据链路上的所有任务。
规范化管理制度
讲到这儿,你可能会问:数据质量取决于稽核规则的完善性,如果数据开发没有添加,或者添加的规则不全,是不是就达不到早发现、早恢复?这个问题戳中了要害,就是规则的完备性如何来保障。在我看来,这不仅仅是一个技术问题,也涉及管理。在网易,我们会制定一些通用的指导规则(比如,所有数据中台维护的表都需要添加主键唯一性的监控规则),但这些通用规则往往与业务关系不大。如果涉及业务层面,就要由数据架构师牵头,按照主题域、业务过程,对每个表的规则进行评审,决定这些规则够不够。那我想建议你,如果要做稽核校验,可以通过组建数据架构师团队,由这个团队负责核心表的规则审核,确保规则的完备性。那么当你按照这几个方法建立了数据质量体系之后,要如何验证体系是否有效呢?
如何衡量数据质量?
做数据治理,我一直奉行“效果可量化”的原则,否则这个治理做到什么程度,很难衡量。那么如何评价数据质量是否有改进呢?除了故障次数,你还可以有这样几个指标。4 点半前数据中台核心任务产出完成率。这个指标是一个综合性指标,如果任务异常,任务延迟,强稽核规则失败,都会导致任务无法在规定时间前产出。基于稽核规则,计算表级别的质量分数。根据表上稽核规则的通过情况,为每个表建立质量分数,对于分数低的表,表负责人要承担改进责任。需要立即介入的报警次数,通常以开启循环报警的电话报警次数为准。对于核心任务,任务异常会触发循环电话报警,接到报警的数据开发需要立即介入。数据产品 SLA。每个数据产品上所有指标有没有在 9 点产出,如果没有,开始计算不可用时间,整体可以按照不同数据产品的重要性进行折算,99.8% 是数据产品一个相对比较好的 SLA。
不过,技术和规范最终需要依靠产品来帮助落地,在网易内部,有一个数据质量中心的 产品,通过介绍这个产品,我希望能给你一个参考,如何去设计一个数据质量中心,或者在选型的时候,数据质量中心必须具备的功能。
成本优化
在一开始建设数据中台时,你往往会关注新业务的接入,数据的整合,数据价值的挖掘上,忽略成本管控的问题,从而落入陷阱中,造成成本爆炸式的增长。所以,你有必要深入了解一下有哪些陷阱,从而尽量在日常开发中避免。在这里,我总结了 8 种陷阱,其中:
1~3 是广泛存在,但是容易被忽略的,需要你格外注意;
4~8 涉及数据开发中一些技能,你在开发过程中注意一下就可以了。
除此之外,在学习这部分知识的过程中,我建议你“知其然,更要知其所以然”,这样才能发现问题的本质,从而深入掌握解决问题的方法。
第一,数据上线容易下线难。
先来看一组统计数据,这是某数据中台项目,表相关的使用统计。从中你可以发现,有一半的表在 30 天内都没有访问,而这些表占用了 26% 的存储空间。如果我们把这些表的产出任务单独拎出来,在高峰期需要消耗 5000Core CPU 的计算资源,换算成服务器需要 125 台(按照一台服务器可分配 CPU 40Core 计算),折合成本一年接近 500W。是不是觉得自己竟然有这么多没用的数据?我经常把数据比作手机中的图片,我们总是不断地拍照,生成图片,却懒得清理,最终手机里面的存储经常不够用。对于无法及时清理数据,数据开发其实也有苦衷。他们并不知道一个表还有哪些任务在引用,还有哪些人在查询,自然不敢停止这个表的数据加工,那造成的后果就是数据上线容易,下线难。
第二,低价值的数据应用消耗了大量的资源。
我们的数据看上去每天都在被访问,但究竟产出了多少价值,投入和产出是否匹配呢?作为一个数据部门,我们要问一问自己。我们曾经有一个宽表(拥有很多列的表,经常出现在数据中台下游的汇总层数据中),算上上游加工链路的任务,每天加工这张宽表要消耗 6000 块钱,一年要 200W,可追查后我们发现,这张宽表实际每天只有一个人在使用,还是一个运营的实习生。显然,投入和产出极不匹配。这其实间接说明,数据部门比较关注新的数据产品带给业务的价值,却忽略了已经存在的产品或者报表是否还存在价值,最终导致低价值的应用仍然在大量消耗资源。
第三,烟囱式的开发模式。
烟囱式的开发不仅会带来研发效率低的问题,同时因为数据重复加工,还会存在资源浪费的问题。我们来算一笔账,一张 500T 的表,加工这张表,计算任务需要高峰期消耗 300Core,折合 7 台服务器(按照一台服务器可分配 CPU 40Core 计算),再加上存储盘的成本 (按照 0.7 元 /TB* 天计算),一年需要消耗 40W。而这张表每复用一次,就可以节省 40W 的成本。所以通过模型复用,还可以实现省钱的目的。
第四,数据倾斜。
你肯定听说过木桶效应吧?一个木桶装多少水,主要取决于最短的那块板。对于一个分布式并行计算框架来说,这个效应同样存在。对于 Spark 计算引擎来说,它可以将海量的数据切分成不同的分片(Partition),分配到不同机器运行的任务中,进行并行计算,从而实现计算能力水平扩展。但是整个任务的运行时长,其实取决于运行最长的那个任务。因为每个分片的数据量可能不同,每个任务需要的资源也不相同。由于不同的任务不能分配不同的资源,所以,总任务消耗资源 =max{单个任务消耗的资源} * 任务数量。这样一来,数据量小的任务会消耗更多的资源,就会造成资源的浪费。
第五,数据未设置生命周期。
一般原始数据和明细数据,会保留完整的历史数据。而在汇总层、集市层或者应用层,考虑到存储成本,数据建议按照生命周期来管理,通常保留几天的快照或者分区。如果存在大表没有设置生命周期,就会浪费存储资源。
第六,调度周期不合理。
通过这张图你可以看到,大数据任务的资源消耗有很明显的高峰和低谷效应,一般晚上 12 点到第二天的 9 点是高峰期,9 点到晚上 12 点,是低谷期。虽然任务有明显的高峰低谷效应,但是服务器资源不是弹性的,所以就会出现服务器在低谷期比较空闲,在高峰期比较繁忙的情况,整个集群的资源配置取决于高峰期的任务消耗。所以,把一些不必要在高峰期内运行任务迁移到低谷期运行,也可以节省资源的消耗。
第七,任务参数配置。
任务参数配置的不合理,往往也会浪费资源。比如在 Spark 中,Executor 内存设置的过大;CPU 设置的过多;还有 Spark 没有开启动态资源分配策略,一些已经运行完 Task 的 Executor 不能释放,持续占用资源,尤其是遇到数据倾斜的情况,资源浪费会更加明显。
第八,数据未压缩。
Hadoop 的 HDFS 为了实现高可用,默认数据存储 3 副本,所以大数据的物理存储量消耗是比较大的。尤其是对于一些原始数据层和明细数据层的大表,动辄 500 多 T,折合物理存储需要 1.5P(三副本,所以实际物理存储 5003),大约需要 16 台物理服务器(一台服务器可分配存储按照 128T 计算),如果不启用压缩,存储资源成本会很高。另外,在 Hive 或者 Spark 计算过程中,中间结果也需要压缩,可以降低网络传输量,提高 Shuffer (在 Hive 或者 Spark 计算过程中,数据在不同节点之间的传输过程) 性能。你看,我为你列举了 8 个典型的成本陷阱,那你可能会问了,老师,我已经中招了,该怎么办呢? 别急,接下来我们就看一看,如何进行精细化的成本管理。
如何实现精细化成本管理?
我认为,成本治理应该遵循全局盘点、发现问题、治理优化和效果评估四个步骤。
全局资产盘点
精细化成本管理的第一步,就是要对数据中台中,所有的数据进行一次全面盘点,基于元数据中心提供的数据血缘,建立全链路的数据资产视图。
从这个图中你可以看到,全链路数据资产视图的下游末端关联到了数据应用(报表:财务分析),而上游的起点是刚进入数据中台的原始数据。数据之间通过任务进行连接。接下来,我们要计算全链路数据资产视图中,末端数据的成本和价值(末端数据就是加工链路最下游的表,例如图中 TableA,Table G)。为什么一定要从末端开始呢? 因为中间数据,在计算价值的时候,还要考虑下游表被使用的情况,比较难计算清楚,所以我们选择从末端数据开始。这与我们下线表的顺序也是一致的,如果数据的价值很低,成本很高,我们也是从末端数据开始下线的。
那么数据成本该如何计算呢?
我们要对上图中财务分析报表核算成本,这个报表上游链路中涉及到 a,b,c,3 个任务,A,B,C,D,E,F, 6 张表,那么:
这张报表的成本 =3 个任务加工消耗的计算资源成本 +6 张表消耗的存储资源的成本。
另外,需要注意的是,如果一个表被多个下游应用复用,那这个表的存储资源成本以及产出任务消耗的成本,需要分摊给多个应用。
那价值又该如何计算呢?
我们来分析一下这张图。如果末端数据是一张应用层的表,它对接的是一个数据报表,那衡量这个数据的价值,主要是看报表的使用范围和使用频率。在计算使用范围时,通常用周活来评估,同时还要考虑不同管理级别的人权重,对于老板,他一个人的权重可以相当于 1000 个普通员工。之所以这样设计,是考虑到管理级别越高,做出的商业决策影响就越大,自然这个价值也就越大。使用频率一般使用单个用户每周查看报表的次数来衡量,次数越高,说明报表价值越大。如果末端数据对接的不是一个数据报表,而是面向特定场景的数据应用(比如我之前提到过的供应链分析决策系统,它面向的人群主要是供应链部门)。衡量这类产品的价值,主要考虑目标人群的覆盖率和直接业务价值产出。什么是直接业务价值产出呢?,在供应链决策系统中,就是通过系统自动生成的采购订单占所有采购订单的比例。除此之外,末端数据,可能还是一张集市层的表,它主要用于提供给分析师做探索式查询。这类表的价值主要看它被哪些分析师使用,使用频率如何。同样,在使用范围评估时,要对分析师按照级别进行加权。
发现问题
全局盘点,为我们发现问题提供了数据支撑,而你需要重点关注下面三类问题:
持续产生成本,但是已经没有使用的末端数据(“没有使用”一般指 30 天内没有访问);
数据应用价值很低,成本却很高,这些数据应用上游链路上的所有相关数据;
高峰期高消耗的数据。
那么为什么你要关注这三类数据呢?
其实第一类就是没有使用,但一直在消耗成本的表,对应的就是我提到的陷阱 1。
第二类其实就是低价值产出,高成本的数据应用,对应的是陷阱 2。
第三类高成本的数据,对应的就是陷阱 4~8。
治理优化
针对这三类问题,我们需要制订相应的策略。对于第一类问题,应该对表进行下线。 数据下线要谨慎,你可以参考这张数据下线的执行过程图:
末端数据删除后,原先末端数据的上游数据会成为新的末端数据,同样还要按发现问题到治理优化进行重复,直到所有的末端数据都不满足下线策略为止。对第二类问题,我们需要按照应用粒度评估应用是否还有存在的必要。对于报表,可以按照 30 天内没有访问的应用自动下线的策略,先对报表进行销毁,然后对报表上游的表进行下线,如果该表还被其他的应用引用,就不能下线。下线步骤可以参考前面的下线步骤。第三类问题,主要是针对高消耗的数据,又具体分为产出数据的任务高消耗和数据存储高消耗。对于产出任务高消耗,首先要考虑是不是数据倾斜。具体怎么判断呢?其实你可以通过 MR 或者 Spark 日志中,Shuffer 的数据量进行判断。如果有某一个 Task 数据量非常大,其他的很少,就可以判定出现了数据倾斜。
治理效果评估
现在,通过我介绍的这几个方法,你已经能够节省大量的资源消耗,那如何量化我们的治理成果呢?五个字:省了多少钱。不过,如果直接拿服务器的数量来衡量,其实并不能真实地反应治理效果,因为还要考虑业务增长的原因。业务不是停止不动的,所以你可以围绕任务和数据的成本考虑这样几点:
下线了多少任务和数据;这些任务每日消耗了多少资源;数据占用了多少存储空间。拿这些资源来计算成本,这样就能够算出来省了多少钱。我还是拿本节课开始的例子来看,任务 A 运行时长 3 个小时,在运行过程中,共消耗 5384503 cpu*s,37007892 GB *s, 假设我们 1 个 CU (1 cpu, 4g memeory)一年是 1300 元成本,折合每天为 3.5 元(计算公式为 1300/365)。
数据服务的问题
服务化在业务系统中提的比较多,它是业务系统化繁为简,实现业务拆分的必经之路(特别是这几年微服务的概念深入人心)。那对于数据中台,服务化意味着什么呢?数据服务到底解决了什么问题? 我相信很多人会有这样的疑问。服务化:不同系统之间通过服务方式交互,服务通常以 API 接口形式存在。当然,关于数据服务的“料”很多,信息比较密集,所以我会用两讲的时间帮你搞清楚这部分内容,今天咱们先来从问题入手,看一看数据服务解决了什么问题,打消你“为什么要有数据服务”这样的疑问。在我看来,要想搞清楚数据服务解决了什么问题,就要先知道,没有数据服务,我们在日常数据建设中存在哪些痛点。
数据接入方式多样,接入效率低
数据中台加工好的数据,通常会以 Hive 表的形式存储在 HDFS 上。如果想直接通过数据报表或者数据产品前端展现,为了保证查询的速度,会把数据导出到一个中间存储上:
数据量少的可以用 MySQL , Oracle 等 DB,因为部署维护方便、数据量小、查询性能强。比如数据量小于 500W 条记录,建议使用 DB 作为中间存储;
涉及大数据量、多维度查询的可以用 GreenPlum,它在海量数据的 OLAP(在线分析处理)场景中有优异的性能表现。比如数据量超过 500W 记录,要进行多个条件的过滤查询;
涉及大数据量的单 Key 查询,可以用 HBase。在大数据量下,HBase 拥有不错的读写性能。比如超过 500W 记录,根据 Key 查询 Value 的场景。如果需要用到二级索引,由于 HBase 原生不支持二级索引,所以可以引入 ES,基于 ES 构建二级索引和 RowKey(HBase 中的 Key)映射关系,查询时先根据二级索引在 ES 中找到 RowKey,然后再根据 RowKey 获取 HBase 中的 Value 值。
因为不同的中间存储,涉及的访问 API 也不一样,所以对数据应用开发来说,每个数据应用都要根据不同的中间存储,开发对应的代码,如果涉及多个中间存储,还需要开发多套代码,数据接入效率很低。而数据服务为数据开发屏蔽了不同的中间存储,应用开发使用统一的 API 接口访问数据,大幅度提高了数据应用的研发效率。
数据和接口没有办法复用
在上图中,当我们开发“数据应用 - 经营分析”时,数据开发会基于 a 表加工 c 表,然后数据应用开发会把 a 和 b 的数据导出到“数据应用 - 经营分析的数据库 db1”中,然后开发经营分析的服务端代码,通过接口 1 对 web 提供服务。当我们又接到任务开发“数据应用 - 毛利分析”时,我们同样需要用到 b 表的数据,虽然 b 的数据已经存在于 db1 中,但 db1 是“数据应用 - 经营分析”的数据库,无法共享给“数据应用 - 毛利分析”(因为不同应用之间共享数据库,会存在相互影响)。同时,经营分析的服务端接口也无法直接给毛利分析用,因为接口归属在经营分析应用中,已经根据应用需求高度定制化。所以我们看到这样的现象:即使数据重复,不同数据应用之间,在中间存储和服务端接口上,也是无法复用的。这种烟囱式的开发模式,导致了数据应用的研发效率非常低。而数据服务,使数据中台暴露的不再是数据,而是接口,接口不再归属于某个数据应用,而是在统一的数据服务上。这就使接口可以在不同的数据应用之间共享,同时因为数据服务具备限流的功能,使接口背后的数据共享成为可能,解决了不同应用共享数据相互影响的问题。那么当数据应用上线之后,我们就进入了运维阶段,如果这个阶段没有数据服务的话,会出现什么问题呢?
不知道数据被哪些应用访问
举个例子,某个技术人员她突然接到了一堆电话报警:有大量的任务出现异常(对应上图中红色表的产出任务)。经过紧张的定位后,她确认问题来源于业务系统的源数据库,也就是说,因为一次数据库的表结构变更,导致数据中台中,原始数据清洗出现异常,从而影响了下游的多个任务。这时,摆在她面前的,是一堆需要恢复重跑的任务。可是队列资源有限,到底先恢复哪一个呢? 哪个任务最终会影响到老板第二天要看的报表呢?虽然数据血缘建立了表与表之间的链路关系,但是在表的末端,我们却不知道这个表被哪些应用访问,所以应用到表的链路关系是断的。当某个任务异常时,我们无法快速判断出这个任务影响了哪些数据应用,从而也无法根据影响范围决定恢复的优先级,最终可能导致重要的报表没有恢复,不重要的报表却被优先恢复了。同样,在成本治理中,我也提到,因为没有应用和数据的链路关系,我们不敢贸然下线数据。而数据服务打通了数据和应用的访问链路,建立了从数据应用到数据中台数据的全链路数据血缘关系,这就等于我们在迷宫中拿到了一个地图,当任何一个任务出现问题,我们都可以顺着地图,找到这个故障影响了哪些应用,从而针对重要应用加速恢复速度。同样,我们也可以放心的下线数据中台中任意一张表。除了不知道数据被哪些下游应用使用,在运维阶段,我们还经常面临着数据表频繁重构,而这也许是数据应用开发最可怕的噩梦了。
数据部门字段变更导致应用变更
数据中台底层模型的字段变更是比较频繁的一个事情,因为本身汇总层的模型也在随着需求不断优化。“数据应用 - 经营分析”使用了数据中台的 ads_mamager_1d 这张表的 c 字段,如果我们对这张表进行了重构,访问字段需要替换成 e 字段,此时需要数据应用修改代码。这种因为数据中台的数据变更导致应用需要重新上线的事情,是非常不合理的,不但会增加应用开发额外的工作量,也会拖累数据变更的进度。有了数据服务,就会把数据应用和中台数据进行解耦,当中台数据表结构变更时,我们只需要修改一下数据服务上接口参数和数据字段的映射关系就可以了。不需要再修改代码,重新上线数据应用。
数据服务应该具备的七大功能
那么为了让你更好地理解数据服务的功能,我来讲个小故事。你肯定去过菜鸟驿站取快递吧?假设有一个很大的菜鸟驿站,里面有很多组货架,每个货架前都有一些工作人员帮助我们取快递,同时也有很多队伍排队。取快递,要先约定好接口(比如统一使用收货码来取货)。然后,为了保证不同队伍都能取到快递,我们要对每个队伍做一些限流(比如一个队伍一次只能取一个人)。在你取走快递时,驿站会记录是谁取走了哪个快递,方便后续追查。这段时间,菜鸟驿站服务开始升级,不仅可以取快递,还提供快递送货上门的服务。除此之外,不同种类的快递对应的货架也变得不同,比如生鲜食品,货架是冷藏冰箱,文件、信封,货架就是文件柜。对于取快递的人来说,如果他买了生鲜,又买了信封,那他要排好几个队伍,肯定不方便。所以,一般来讲,取快递的人最好只在一个队伍排队,而驿站工作人员帮他一次把多个货架的快递都取过来。可驿站的货架实在是太多了,为了方便每个取快递的小伙伴都能快速找到每个货架以及队伍,驿站提供了一个导览。与此同时,为了不让工作人员出错,驿站的工作人员必须经过严格的测试,才能上岗。讲完这个故事之后,我们接着回到数据服务的这八大功能上来。在取快递的这个例子中,你可以把数据服务看成是一个菜鸟驿站,工作人员看成是 API 解耦库,货架可以看作是中间存储,快递则可以认为是数据。那么对应到七个功能,就是:
接口规范化定义,可以看成是取快递约定的收货码,基于统一的收货码取走快递;数据网关,可以看成是我们对每个货架前的队伍进行限流,确保每个队伍都能取走快递;链路关系的维护,可以看作是驿站会记录谁取走了什么快递;数据交付,可以看作驿站同时提供取快递和送货上门服务;提供多样中间存储,可以看成有不同类型的货架;逻辑模型,可以看成是一个工作人员,可以取多个货架的快递;API 接口,可以看作是驿站的不同货架的不同队伍导览。
第一个是接口规范化定义。
接口规范化定义就是取快递时我们约定的取件码。数据服务,对各个数据应用屏蔽了不同的中间存储,提供的是统一的 API。
第二,数据网关。作为网关服务,数据服务必须要具备认证、权限、限流、监控四大功能,这是数据和接口复用的前提。这就跟我们在菜鸟驿站前取快递,要对每个队伍的人进行认证、限流一个道理。我详细介绍一下。首先是认证,为了解决接口安全的问题,数据服务首先会为每个注册的应用分配一对 accesskey 和 secretkey,应用每次调用 API 接口,都必须携带 acesskey 和 secretkey。除此之外,对于每个已发布的 API,API 负责人可以对应用进行授权,只有有权限的应用才可以调用该接口。同时,API 接口的负责人可以对应用进行限流(例如限制每秒 QPS 不超过 200),如果超过设定的阈值,就会触发熔断,限制接口的访问频率。需要你注意的是,对于接口复用来说,限流功能非常必要,否则会造成不同应用之间的相互影响。当然,数据服务还要提供接口相关的监控,比如接口的 90% 的请求响应时间、接口调用次数、失败次数等相关的监控,另外,对于长时间没有调用的 API ,应该予以下线。这样做的好处是防止没用的接口额外占用资源。
第三,全链路打通。
数据服务还必须负责维护数据模型到数据应用的链路关系。在上图中,经营分析是一个数据应用,甄美丽是数据应用的开发,当她想要访问数据服务中的某个接口获取表 A 和 B 的数据时,她需要向接口的发布者马帅帅申请授予权限。然后经营分析就可以通过接口获取到数据。同时,数据服务会把经营分析和表 A 和 B 的访问关系,推送给数据中台的元数据中心。接着元数据中心表 A、B 以及 A 和 B 的上游所有的表(图中 D 和 E)上,就会有经营分析数据应用的标签。当表 D 的产出任务异常时,马帅帅可以通过元数据中心,快速判断出该任务影响了经营分析数据产品的数据产出。同时,当马帅帅想要下线表 D 时,也可以通过这张表是否有标签,快速判断这个表下游是否还有应用访问。当马帅帅取消 API 接口授权时,元数据中心同时会清理表的相关标签。需要特别提到的是,一个数据应用往往涉及很多页面,如果我们在影响分析时,只分析到应用,可能粒度还是太粗了,需要到更细级别的页面的粒度,比如一个任务异常,我不光要知道是哪个数据产品,还必须得知道是哪个数据产品的哪个页面。此时,我们在接口授权时,可以标注页面名称。
第四,推和拉的数据交付方式。相信你听到的数据服务,都是以 API 接口的形式对外提供服务,但是业务实际场景中,光 API 还不够的。我把 API 方式称为拉的方式,而实际业务中同样还需要推的场景。比如在实时直播场景中,商家需要第一时间获得关于活动的销售数据,此时就需要数据服务具备推的能力,我把它称为数据的送货上门服务。数据服务将数据实时写入到一个 Kafka 中,然后应用通过订阅 Kafka 的 Topic,可以获得实时数据的推送。
第五,利用中间存储,加速数据查询。数据中台中数据以 Hive 表的形式存在,基于 Hive 或者是 Spark 计算引擎,并不能满足数据产品低延迟,高并发的访问要求,所以,一般做法是将数据从 Hive 表导出到一个中间存储,由中间存储提供实时查询的能力。数据服务需要根据应用场景支持多种中间存储,我列举了一些常用的中间存储以及这些存储适用的场景,希望你能根据实际场景选择适合的中间存储。
第六,逻辑模型,实现数据的复用。
在前面取快递的场景中,每一个货架一拨工作人员,其实对取快递的人并不友好,所以最好的就是一个人帮我们把所有的快递都取了。这就有点儿类似数据服务中逻辑模型的概念了。我们可以在数据服务中定义逻辑模型,然后基于逻辑模型发布 API,逻辑模型的背后实际是多个物理表,从用户的视角,一个接口就可以访问多张不同的物理表了。逻辑模型可以类比为数据库中视图的概念,相比于物理模型,逻辑模型只定义了表和字段的映射关系,数据是在查询时动态计算的。逻辑模型可以看作是相同主键的物理模型组成的大宽表。逻辑模型的存在,解决了数据复用的问题,相同的物理模型之上,应用可以根据自己的需求,构建出不同的逻辑模型,每个应用看到不同的列。
第七,构建 API 集市,实现接口复用。为了实现接口的复用,我们需要构建 API 的集市,应用开发者可以直接在 API 集市发现已有的数据接口,直接申请该接口的 API 权限,即可访问该数据,不需要重复开发。需要特别指出的是,数据服务通过元数据中心,可以获得接口访问的表关联了哪些指标。使用者可以基于指标的组合,筛选接口,这样就可以根据想要的数据,查找可以提供这些数据的接口,形成了一个闭环。
数据服务系统架构设计
大部分公司在实现数据服务时,主要采用了云原生、逻辑模型和数据自动导出三个关键设计,关于这部分内容,我希望你能通过学习,在实际工作中可以借鉴我们的方式完成数据服务的设计,或者在选择商业化产品时,给你一个架构选型方面的参考。
云原生
云原生的核心优势在于每个服务至少有两个副本,实现了服务的高可用,同时根据访问量大小,服务的副本数量可以动态调整,基于服务发现,可以实现对客户端透明的弹性伸缩。服务之间基于容器实现了资源隔离,避免了服务之间的相互影响。这些特性非常适用于提供高并发、低延迟,在线数据查询的数据服务。
上图是数据服务的部署架构,在这个图中,每个已经发布上线的 API 接口都对应了一个 Kubernates 的 Service,每个 Service 有多个副本的 Pod 组成,每个 API 接口访问后端存储引擎的代码运行在 Pod 对应的容器中,随着 API 接口调用量的变化,Pod 可以动态的创建和销毁。Envoy 是服务网关,可以将 Http 请求负载均衡到 Service 的多个 Pod 上。Ingress Controller 可以查看 Kubernates 中每个 Service 的 Pod 变化,动态地将 Pod IP 写回到 Envoy,从而实现动态的服务发现。前端的 APP,Web 或者是业务系统的 Server 端,通过一个 4 层的负载均衡 LB 接入到 Envoy。基于云原生的设计,解决了数据服务不同接口之间资源隔离的问题,同时可以基于请求量实现动态的水平扩展。同时借助 Envoy 实现了限流、熔断的功能。你也可以借鉴我们的方案,实现原原生的数据服务设计。
逻辑模型
相较于物理模型,逻辑模型并没有保存实际的数据,而只是包括了逻辑模型和物理模型的映射关系,数据在每次查询时动态生成。逻辑模型的设计,解决了不同接口,对于同一份数据,需要只看到自己需要的数据的需求。下图是网易数据服务逻辑模型的系统设计图。
接口发布者在数据服务中选择主键相同的多张物理表构建一个逻辑模型,然后基于逻辑模型发布接口。API 服务接到查询请求后,根据逻辑模型和物理模型字段的映射关系,将逻辑执行计划拆解为面向物理模型的物理执行计划,并下发多个物理模型上去执行,最后对执行的结果进行聚合,返回给客户端。一个逻辑模型关联的物理模型可以分布在不同的查询引擎上,但是这种情况下,考虑性能因素,只支持基于主键的筛选。
数据自动导出
数据服务选择的是数据中台的一张表,然后将数据导出到中间存储中,对外提供 API 。那数据什么时候导出到中间存储中呢? 要等数据产出完成。所以在用户选择了一张数据中台的表,定义好表的中间存储后,数据服务会自动生成一个数据导出任务,同时建立到这个数据中台表的产出任务的依赖关系,等到每次调度产出任务结束,就会触发数据导出服务,将数据导出到中间存储中,此时 API 接口就可以查询到最新的数据。
总结
以后关于数据中台系列的总结大部分来自Geek Time的课件,大家可以自行关键字搜索。