1 背景介绍
如今大部分大公司内部的数据集都会越来越多,甚至到了暴涨的地步。这些数据集与源代码、基础设施都是公司的重要资产。虽然公司具有代码开发工具、流程管理工具,但是并没有标准的数据集管理方法。随着数据集数量的增长,生产力流失、重复建设、数据滥用的风险会越来越高,所以建设一个标准、灵活的数据集管理系统是当务之急。
图1
EDM(Enterprise Data Management)是在企业中组织数据集的一种通用方式。然而,在使用EDM的情况下,公司使用EDM来发布、检索和整合数据集。另一种完全相反的思路,就是允许企业完全自由地访问和产生数据集,通过某种事后型的手段来解决“找数据”的问题。这种思路类似数据湖(data lakes)的概念,湖中囊括公司内部产生的全部数据集,而目标就是在需要的时候从湖中钓出正确的数据集。
图2
GOODS(Google Dataset Search)就是一个为了组织Google内部数据集而搭建的一个事后型系统。GOODS在后台通过非侵入的方式收集数据集的元数据以及用法,然后赋能一些服务,方便Google的工程师们查找他们的数据集。图3是GOODS的一个概览图,GOODS从不同的存储系统和基础应用中爬取数据集的元数据(owner、访问时间、内容特点、生产pipeline的访问),聚合到中央目录中,并将特定数据集的元数据与其他数据集的信息相关联。
图3
可以看到,GOODS的核心是目录(Catalog),在目录之上,赋能了一些服务,包括:
-
团队大盘。展示团队的全部数据集,归类浏览(owner、数据中心、schema),在数据集大小、内容或可用性发生非预期变化时发出告警。
-
数据来源。数据的上下游信息,一个指定的数据集由哪些数据集产生(上游),又被哪些数据集使用(下游),形成依赖关系。发现数据集出现问题时,可以检查数据来源视图,确定是否某个上游数据集的变更引发了这个问题。同样地,如果团队想对他们的pipeline做出较大变更或者在已有的数据集中发现了BUG,他们可以快速将影响通知下游。
-
数据发现。GOODS提供了搜索引擎,可以检索到Google的全部数据集。还为每个数据集呈现一个简介页面,展示schema、用于访问或查询数据的样本代码以及内容与当前数据集相似的其他数据集信息。相似性信息便于实现数据聚合,例如:如果两个数据集有同样的主键列,那么他们的内容可能是互补的,可以作为join操作的候选对象。
-
数据共享和交换。GOODS允许用户扩充目录中的元数据。例如:数据集owner可以添加注释(例如,数据集使用了哪些分析技术,需要小心哪些陷阱),帮助用户确定是否适合其使用。审计人员可以打标包含敏感信息的数据集,并向数据集owner发出警告,或者提醒复查以确保数据处理得当。GOODS也暴露了注册元数据的API。
2 六大挑战
GOODS在设计和建设的过程中遇到了很多挑战。虽然某些特点是Google Data Lake特有的,但这些经验和教训也适用于大多数其他的大型企业。
图4
2.1 数据集规模(数量和大小)
Google现在数据集的数量已经远远超出了之前的预测。现在Catalog索引了超过260亿数据集,其中只包括所有Google工程师都有读访问权限的数据集,排除了许多不感兴趣的数据集(比如空文件),并且做了路径(path)标准化来避免冗余(同一数据集的不同shard的路径标准化为一个公共路径,而不是在目录中分开存储)。如果索引了访问权限受限的数据集,并支持其他存储系统,Catalog的数据集数量将不止翻倍。
在这种规模下,收集所有数据集的元数据是不可行的。假设每个数据集处理耗费1秒(许多较大数据集的处理耗时不止1秒),使用1000台机器并发遍历有260亿数据集的目录,需要大约300天。因此,需要制定数据集处理的优先策略和优化策略。
元数据推导中的“N次方”问题还加剧了规模问题。例如,GOODS需要识别全部列或个别列包含相似或相同内容的数据集。在数据集较大的情况下,比较两个数据集的成本很高,两两成对比较几十亿的数据集内容显然是不可能的。
图5
2.2 多样性
数据集以多种格式(txt、csv、Bigtable等)存储在多种存储系统(GoogleFS、数据库服务等)中,每个都有自己的元数据类型和访问特性。所以很难定义一个数据集概念来覆盖所有实际的数据集类型。对用户屏蔽这种多样性和复杂性,并提供统一的方法访问和查询所有类型数据集的信息,是GOODS的目标和挑战。
根据数据集类型、大小以及元数据的不同,元数提取成本也是多样的。因此,需要区别对待元数据提取过程:识别哪些数据集是重要的,并基于持有某个类型元数据的成本和收益来执行元数据推导。
多样性还体现在数据集之间的关系中。例如一个Bigtable数据集,它由若干个列族组成,每个列族继承Bigtable的元数据,也包含自己的元数据和访问属性。所以我们既可以将列族视为独立的数据集,也可以将它视为Bigtable数据集的一部分。Bigtable底层是分布式文件系统,虽然也可以将对应的文件视为数据集,但是可以屏蔽底层文件,只理解Bigtable数据集。但是在Dremel表场景中,由于文件和表的访问方式差异很大,Catalog需要文件和数据库表视为两个数据集。
图6
2.3 目录条目变动
每天都有新数据集产生,也有旧数据集被删除(显示删除或过期)。Catalog中,每天有至少5%(大约10亿)的数据集被删除,又有同样多条目被添加回来。在这种级别的变动下,需要思考哪些数据集需要计算元数据,哪些数据集需要包含到目录中。比如,许多数据集是大型生产pipeline的中间结果,TTL很短,几天后就被回收了。那么一种处理方式是忽略这些临时数据集的元数据,或者直接从目录中排除掉。但是还有两个因素要考虑。首先,这类数据集中某些TTL很长(几周),刚刚创建后对用户的价值很高。其次,一些临时数据集连接了非临时数据集,对数据集来源的计算非常重要。因此,GOODS没有使用黑名单机制处理临时数据集。
2.4 元数据的不确定性
GOODS通过一种事后的非侵入的方式显示地识别和分析数据集,没办法完全确定所有类型的元数据。例如许多数据集中数据的格式是protocal buffer,数据集本身无法知道使用的哪个特定的protocal buffer,这种protocal buffer和数据集的关系隐含在访问数据集的代码中。GOODS通过几种方式来挖掘这种关系:将数据集内容与Google内注册的所有protocal buffer类型匹配,或者查询可能记录了实际protocal buffer的使用日志。这种推理可能导致多种匹配。
2.5 计算数据集的重要性
推理数据集对用户的重要性也是一种挑战。首先,“是什么让一个数据集很重要”这个问题很难回答。通常需要站在一个全局的角度来理解数据集的重要性(例如,考虑生产pipeline访问数据集的频率)。除了重要性之外,还需要不同的重要性等级,用于对数据集进行优先级排序来推导元数据。例如,通常临时数据集是不重要的,但是如果临时数据集连接了非临时的数据集,它的重要性就会相应提高。
2.6 恢复数据集语义
理解数据集内容的语义对于搜索、排名和描述数据集非常有用。假设知道一个数据集的schema,其某些属性是整型值,通过一些对该数据集内容的引用,知道了这些整型值是已知的地标ID。当用户通过GOODS搜索地理数据时,可以使用这种类型的内容语义来改进搜索。将抽象级别从原始字节提升到语义概念,可以推导出更加深刻、清晰的数据集元数据。但是,数据中几乎没有足够的信息来识别语义。
Number of datasets | 26 billion |
Number of paths added per day | 1.6 billion |
Number of paths deleted per day | 1.6 billion |
Number of storage systems | 6 |
Number of dataset formats | > 20 |
表1
3 谷歌目录
Catalog是GOODS的核心,涵盖了从Google不同存储系统爬取来的数据集,提供了可用数据集的全局统一视图。即使没有图1顶部的服务,它本身也是一个重要的贡献。
GOODS还将数据集组织为集群(cluster),并将集群作为目录中的一级条目。在数据集多版本情况下,用户希望将多个版本视为一个逻辑上的数据集,并且这些版本通常具有一些相同的元数据(owner、schema),分别收集每个版本不如将多个版本组织为集群(逻辑数据集)。
3.1 元数据
GOODS通过爬取和推导两种手段获取元数据。size、owner、reader和访问权限这些基础元数据可以通过爬取获得,但是job、团队、用户、schema这些元数据一般存储系统没有记录,这些信息分布在日志中、编码在数据集中或者要从数据内容分析。而GOODS就是一个统一的收集和访问元数据的平台。
Metadata Groups | Metadata |
Basic | size, format, aliases, last modified time, access control lists |
Content-based | schema, number of records, data fingerprint, key field, frequent tokens, similar datasets |
Provenance | reading jobs, writing jobs, downstream datasets, upstream datasets |
User-supplied | description, annotations |
Team and Project | project description, owner team name |
Temporal | change history |
表2
(1)基础元数据
基础元数据包括时间戳、文件格式、owner和访问权限,爬取而来,通常不需要推导。GOODS其他模块的行为通常会依赖这部分信息,比如绕过访问受限或者近期没有变更过的目录条目。
(2)来源(provenance)
通过数据集如何产生以及如何使用等信息,可以更好的理解数据集。这些信息也可以跟踪数据在企业内的跨团队流动。GOODS的Catalog维护的provenance包括数据集如何被生产、如何被消费、依赖的数据集、被依赖哪些数据集依赖、依赖关系的最早时间点、依赖关系的最新时间点。
provenance的产出过程:
-
从生产日志中获取哪些任务在读、写数据集的信息;
-
连接数据集和任务,形成数据集之间的传递闭环。
例如,Job J读取数据集D1并生产数据集D2,那么D1的元数据中,D2就是D1的下游之一,反之亦然。
然而数据访问事件量非常大,传递闭环的级数也可能非常多。所以GOODS仅对数据访问事件日志进行采样处理,并且只实现若干级传递闭环。
图7
(3)schema
schema是理解数据集的另一个重要的元数据类型。Google内部最常用的数据集格式都不是自描述的,需要推导schema。Google绝大部分结构化数据集的内容都编码为可序列化的protocal buffer,难点就是确定数据集使用的是哪个protocal buffer。因为大多数Google数据集使用的protocal buffer都来自Google中央代码库,GOODS 将全部可用的protocal buffer与爬取的数据集做匹配。一方面是从文件中扫描一些二进制数据,另一方面是遍历每一个协议消息定义(protocal message definition)来确定其是否有可能生成前面扫描出的二进制数据。所以一个数据集有可能匹配到多个protocal buffer,并附加一个匹配权重。
(4)内容摘要
GOODS对于能够打开和扫描的数据集,会收集其内容摘要元数据。包括:
-
从采样内容中统计频繁使用的token。
-
通过HyperLogLog算法分析一些字段,判断其是否有可能是独立键或组合键。
-
收集内容的fingerprints,识别内容或者列具有相似性或相同的数据集。
(5)用户提供的注解
允许数据集的owner提供文本描述。有助于目录排行,以及过滤一些测试阶段或者不应该展示的数据。
(6)语义
对于schema为protocal buffer的数据集,GOODS会检查定义protocal buffer的源代码并提取注释信息。通过词法分析,就可以得到代表schema语义的词语。比如,一些GOODS中的protocal buffer数据集包含mpn属性,从字面上很难理解,但是源代码在属性上注释了“//Model Product Number”。另外,GOODS也将数据集的内容与Google's Knowledge Graph进行匹配,识别属性的业务实体(位置、交易等)。
(7)其他
除了上述外,GOODS还收集数据集所属的项目描述、变更历史,为其owner团队收集标识符。最后,GOODS还允许团队添加自定义的元数据类型,比如:不同类型的内容摘要,或者额外的provenance信息。
3.2 将数据集组织为集群(cluster)
GOODS目录中的260亿数据集并不是完全孤立的,有定期产生的同一数据集的不同版本、跨数据中心的数据复制、便于快速load的sharding后的较小数据集等。如果可以识别数据集属于哪个集群,既可以为用户提供多版本数据集在逻辑层面的抽象,又可以节省元数据提取成本(以损失精度为代价)。方式就是收集集群中少数数据集的元数据,然后在集群中的其他数据集上应用这些元数据。例如,如果一个JOB每天生成一个数据集版本,那么这些数据集可能有相同的schema,因此无需为每个版本推导schema。同样地,如果用户为一个数据集添加了描述,那么这个描述信息通常可以应用在集群的所有数据集上。当集群规模较大时,节省的计算开销就显而易见了。
通过将数据集组织为集群来节省计算开销的前提是归类集群这个过程本身的代价不大,如果检查每一个数据集的内容,成本还是很高的。GOODS通过数据集的路径(path)来归类集群,路径中通常包含时间戳、版本等标识。例如,按天生成的数据集,其中一个实例的路径是“/dataset/2015-10-10/daily_scan”,可以将日期中的“日”抽象出来,以“/dataset/2015-10-<day>/daily_scan”表示一个月内生成的所有数据集。同理,将“月”也抽象出来,就可以在更高的抽象层次上以“/dataset/2015-<month>-<day>/daily_scan”表示一年内生成的所有数据集。
按照不同的维度和层次抽象,可以构建一个半网格结构,每个节点代表一个不同粒度的数据集视图。图5的例子就是在日期和版本两个维度抽象出的两层半网格结构。
图8
表3列出了GOODS当前使用的抽象维度。对所有的数据集路径按照不同的维度抽象,得到的半网格中,每个非叶子节点都是一个归类集群的不同选择。选择抽象维度的一种方式是构建一个目标函数,该函数基于目录当前的状态执行选择。但是目录的变化会导致抽象维度的变化。而GOODS的做法很简单,效果也很好。GOODS只为半网格最顶层的元素创建目录条目。对于图5的例子来说,目录会为集群“/dataset/<date>/<version>”生成一个条目,代表网格最低端的3个数据集。这种方式的好处是:
(1)集群数量最少;
(2)保证每个数据集只属于一个集群;
(3)集群稳定。
Abstraction Dimension | Description | Examples of paths with instances |
Timestamps | All specifications of dates and times | /gfs/generated_at_20150505T20:21:56 |
Data-center Names | Specification of data center names | /gfs/oregon/dataset |
Machine Names | Hostnames of machines (either user’s orone in the data center) | /gfs/dataset/foo.corp.google.com |
Version | Numeric and hexa-numeric versionspecifications | /gfs/dataset/0x12ab12c/bar |
Universally Unique Identifier | UUIDs as specified in RFC4122[6] | /gfs/dataset/30201010-5041-7061-9081-F0E0D0C0B0AA/foo |
表3
有了集群之后,就可以将单个数据集的元数据聚合成集群元数据。例如,如果知道集群中一些数据集具有相同的schema,那么就可以将该schema作为集群整体的schema,如图4。是否继续传播集群元数据到每个数据集因应用而异。在GOODS中,只计算集群元数据,从而可以在数据集中区分分析而来的元数据和传播来的元数据。
图9
图5展示了GOODS Calalog中每个集群中数据集的数量分布。可以看到,通过归类集群,物理数据集被压缩为更少的逻辑数据集。用户查看目录更容易,也节省了计算元数据的开销。
图10
4 技术实现
4.1 目录存储
GOODS目录使用Bigtable存储,可扩展的、版本(temporal)K-V存储,每行代表一个数据集或者集群,以数据集路径或集群路径(path)作为key。Bigtable提供行级的事务一致性,非常适合GOODS,因为GOODS的大部分处理都是在一个数据集上。比如推导某个数据集的schema,或者分析其内容,都只需要查询单行。
虽然也有一些场景不是单数据集处理,比如将多行信息聚合成抽象网格中的逻辑数据集,以及将集群元数据应用到它所有的数据集上,但是这些并不需要强一致性。
只有批任务(不服务于前端工具)访问的数据保存在单独的列族中,高压缩度,非内存驻留。例如,GOODS最大的列族包含原始provenance数据,这些数据用来计算provenance图,不直接服务于前端,所以可以尽可能压缩这个列族。服务于前端的provenance信息来自于抽象的集群层面。
Calalog将两种元数据保存在Bigtable中:(1)数据集元数据;(2)与各模块处理结果相关的状态元数据。状态元数据会列出与数据集相关的各个模块,包括时间戳、成功状态、错误信息。GOODS通过状态元数据来协调各模块的执行,并进行系统检查(哪些数据集被模块X处理成功了?最常见错误是什么?)。结合Bigtable的版本特性,状态元数据还可以用来调试。GOODS配置Bigtable保存多版本的状态元数据,可以看到各模块随着时间都做了些什么(例如,什么时候开始报错,异常是否必然发生)。
图11
4.2 批任务性能和调度
GOODS有两类JOB:(1)大量不同的批处理JOB;(2)少数服务于前端和API的JOB。另外,GOODS是可扩展的,并可兼容新的爬虫和分析模块。一些批JOB是非常快的,在几个小时内可以完成目录的遍历;其他的,比如分析数据集内容的JOB,需要花费很多天才能将一个新的爬虫数据集添加到目录中。因为数据集是分布在全球各地的,GOODS在离这些数据集最近的地方调度分析JOB。
JOB的运行彼此独立。每个JOB包含一个或多个模块,比如爬虫或分析。
模块之间通常相互依赖的。例如,计算列fingerprints的模块依赖数据集schema,所以会依赖schema分析模块。模块的执行通过状态元数据来协调。假设模块A必选先于模块B执行。当模块B处理某一行的时候,会先检查该行的状态元数据,如果模块A处理成功了则执行,否则跳过,等下次调度再次尝试处理。如果A重新处理了某一行,B也会重新处理。状态元数据还可以用来避免在一定时间内的重复处理。
大部分JOB按天调度,并且可以在24小时之内执行完毕。如果有超过24小时的,就优化JOB或者增加并发度。以24小时为循环向目录中增加新的行,或者更新已经存在的行。
当新增大量数据集时,schema分析这种重量级任务需要几天或几周来完成。GOODS使用简单的优化级机制确保最重要的数据集在这期间按时处理。将有用户注释的数据集和provenance处于较高结点的数据集视为“重要的”,并调度两个JOB:一个只处理重要的数据集,另一个处理所有的数据集。
GOODS的大型爬虫JOB执行“盲写”:所读即所写,Bigtable也不区分插入还是更新。比起先从目录中读取存量数据,再与爬取的新数据关联,“盲写”更有效率。
图12
4.3 容错
GOODS在分析大量各种数据集的同时,会遇到各种问题。对于单独处理数据集的模块,会将异常信息记录在该数据集的状态元数据中,便于重试(有限次数)。对于多行处理的模块,依赖状态元数据中JOB的开始时间以及成功与否。例如,provenance连接模块,比较数据集JOB的时间戳与上一次建立连接的时间,如果前者较新则结合到provenance图中。
GOODS的一些模块依赖不同的三方库查件不同的数据集内容,有时候这些库会崩溃或者死循环。考虑到不能让这些问题影响其他运行时间很长的分析JOB,所以将有这种风险的JOB放在单独的进程中处理,然后使用监控线程将长期停顿转换为崩溃状态。
GOODS目录备份在多个地方。写请求直接访问master,并异步复制到其他地方。
4.4 元数据的垃圾回收
GOODS每天产生大量的数据,其中有一部分是临时的。在使用这些临时数据构建provenance图之后,可以删除其在目录中的条目。GOODS起初的做法是删除一周没有更新过的行,但是引发了一些故障,需要将所有爬虫和非垃圾回收相关的模块禁用几天来恢复。
GOODS现在的垃圾回收机制是:
1 删除行的条件可以从元数据以及状态中获取,从GOODS目录中删除一个数据集的条件:
-
数据集在存储系统中已经被删除。
-
数据集最近更新的provenance信息已经由provenance链接模块处理完成。
2 从目录中删除条目时,要确保不产生“dangling rows”(悬挂行)。因为Bigtable不区分插入和更新,当删除某一行时,必须确保没有并发运行的模块将该行重新插入(带有模块计算后的部分信息)。
3 所有其他模块必须能够独立于垃圾回收运行,并能够与垃圾回收同时运行。
Bigtable支持条件判断。以事务的方式,根据条件来判断执行更新还是删除。但是,所有模块的行更新操作都进行条件判断的开销很大。
GOODS最终的方案是允许非垃圾回收模块执行非事务更新行,垃圾回收分成两个阶段:(a)给满足删除条件的行打标。(b)24小时之后如果该行仍然满足删除条件,执行删除;否则删除打标。
同时,其他模块遵循约束:(a)执行非事务更新;(b)忽略打标删除的行;(c)模块的一次处理不能超过24小时。
5 前端:服务目录
目前为止,我们介绍了GOODS目录的构建和维护。在这一节中,我们介绍GOODS收集来的元数据所赋能的主要服务(图3的顶层部分)。
5.1 数据集概要页面
第一个服务是在易于查看的页面中展示指定数据集的元数据。服务接受数据集或集群的路径作为输入,然后展示对应的元数据。服务还提供方法让用户编辑元数据的某些部分,允许用户扩充或修整目录中的信息。
页面中呈现了第三节中介绍的大部分元数据。当展现页面给用户时,需要在展示详尽信息和保持页面信息量可控之间做权衡。控制信息量是非常重要的,既防止向用户输出大量信息,也避免传输大量信息。例如:provenance。常用的数据集可能被上万个任务读取,具有上万个下游。类似GOODS的任务模块,需要访问公司内的所有数据集,其上游可能存在几十亿数据集。为了避免传输并向用户展示这些大量的信息,我们使用3.2节中介绍的抽象机制离线压缩provenance元数据,然后在页面中呈现这个压缩的provenance。如果压缩后的元数据仍然很大,那么只保留最近的一些条目。
数据集页面将一些元数据和其他更专业的工具连接起来。例如,页面连接provenance元数据与任务中心工具中的任务详情。类似地,连接schema元数据与提供schema定义的代码管理工具。相应地,这些工具连接到GOODS,帮助用户获取关于数据集更多的信息。
页面还提供不同语言(例如,C++、Java、SQL)的代码片段来访问数据集的内容,我们为特定的数据集定制生成代码片段。例如,代码片段使用了数据集的路径和schema(已知时),用户可以在各自的编程环境中复制粘贴这些代码片段。这些代码片段是为了补充页面元数据内容:后者提供了数据内容的schema级信息,而代码片段提供了简单方式来快速查询实际内容或通过代码分析内容。
总之,页面的意图是提供一站式的地方,用户可以在这里查询数据集信息,并且理解数据集在生产中使用的上下文。这些特性使得页面能够在用户之间共享数据集,连接数据集信息与其他工具。Google文件系统浏览器提供了GOODS数据集页面的直连。
5.2 数据集搜索
数据集搜索允许Google的人使用简单的关键字查询数据集。该服务由用于文档检索的传统反转索引支持,每个数据集就是一个“文档”,根据数据集元数据的子集推导索引token,每个token关联索引一个的特定部分。例如,从数据集path推导的token关联索引的path部分。因此,搜索“path:x”将匹配数据集path的关键字“x”,而搜索“x”将匹配数据集元数据的任何部分。表4总结了数据集搜索索引中的主要部分及其含义。
Qualified token | Where token matches |
path:a | Path of the dataset |
proto:a | Name of protocol bu↵er |
read_by:a written_by:a | Names of jobs reading/writing the dataset |
downstream_of:a upstream_of:a | Paths of datasets downstream/upstream of the dataset |
kind:a | Type of the dataset |
owner:a | Owners of the dataset |
表4
提取索引token的时候,必须遵循索引覆盖的查询类型。例如,我们希望在数据集路径上支持部分匹配,当用户搜索“x/y”的时候,匹配路径为“a/x/y/b”的数据集,而不是路径为“a/y/x/b”的。一种方法是对每个子序列进行索引(例如,对于路径“a/x/y/b”提取索引token“a/x”、“x/y”、“a/x/y”、“x/y/b”等等)。然后,这种方法会导致索引数量非常多。相反,我们分隔路径后,将每个token与其在路径中的位置相关联。回到我们的例子,路径“a/x/y/b”映射到有序的索引token“a”、“x”、“y”和“b”。当用户查询部分路径时,我们的服务以相同的方式解析路径,并将查询的token与索引中的连续token相匹配。当索引protocol buffer的名称时,使用同样的方法。可以使用名称空间(例如,“foo.bar.X”)作为protocol buffer名称。以这种方式,用户可以搜索schema与特定命名空间下的protocol buffer匹配的所有数据集。
匹配搜索关键字到数据集只是搜索任务的第一步。第二步是推导一个打分函数来排序匹配的数据集,以便最上面的结果与用户查询是相关的。打分一般是个困难的问题,我们正在进行的一部分工作是根据用户经验来调整打分函数。下面我们描述一些迄今为止的打分函数设计。
(1)数据集的重要性依赖它的类型。例如,打分函数更倾向Dremel表而不是文件数据集,其他的都是平等的。因为数据集owner必须显示注册Dremel表,这就导致其对更多的用户可见。我们将这种行为解释为数据集重要的一个信号,并反映到最终的打分中。
(2)关键字匹配的重要性依赖索引部分。比如,一个关键期匹配数据集路径,比匹配到的读写数据集的job重要,其他的都是平等的。这种做法反映了我们在实践中观察到的搜索类型。
(3)血缘输出是重要性的一个优良指标。特别是有很多读job,并有很多下游的数据集。如果有很多生产pipeline在访问某个数据集,很可能这个数据集很重要。这种方法可以看作页面排行在图中的一个近似,数据集和生产任务是端点,边表示job对数据集的访问。一个问题是,只因为被很多内部pipeline消费,就给数据集打很高的分数,即使它对于用户来说不是有用的。一个例子就是Google web爬取,许多pipeline使用它提取不同类型的信息。因此,调节和控制这种方法对整体排行函数的贡献变得非常重要。
(4)有owner提供描述信息的数据集可能很重要。用户界面允许数据集owner提供数据集的描述信息。我们将这种信息的存在是为数据集重要的一个信号。如果关键词出现在了数据集描述中,这个数据集应该得到更高的权重。
打分函数结合上述几点和其他信号。调节不同信号的贡献一直是我们团队在进行中的工作。我们并没有生成上述提到的方法是完美的。事实上,在搜索时间以及静态、更全局的上下文中,理解影响数据集重要性的因素也是一个有趣的研究课题。这个上下文考虑了数据集的使用与其他数据集、job、企业团队的关系。这个静态上下文与后台相关,是数据集元数据提取优先级的额外信号。
除了关键字搜索,GOODS还为目录中的元数据展示元数据分类,比如owner和文件格式。这些分类为用户提供匹配到的数据集概览,方便了用户后续进一步深入查询。
5.3 团队大盘
GOODS团队大盘是可配置的且一站式的,展示团队产生的数据集及感兴趣的元数据,例如各种健康指标、其他大盘、数据集所在的存储系统是否在线。当更新大盘中数据集的表数据时,GOODS自动更新大盘内容。用户可以将大盘嵌入到文档中,或者分享给其他人。
GOODS大盘还提供数据集监控和报警。用户可以设置监控,然后GOODS会监控数据集的对应属性,并向内部监控UI传递报警。除了执行固定校验,GOODS可以通过学习一些感兴趣的公共属性的趋势来生成校验。例如:如果数据集的大小在历史中每个版本都增长10%,那么GOODS可以推荐相应的校验,下一个数据集大小应该在预期的一个大小左右。
6 经验教训
(1)进化。GOODS目录的构建,最开始是以数据集发现为目标使用场景。很快就发现工程师们用各种各样的方式使用GOODS,以下是从使用日志和用户那里发现的主要趋势:
- 审计protocal buffer。某些protocal buffer包含了私人可识别信息,所以任何使用这些protocal buffer的数据集都必须遵循严格的使用和访问策略。通过GOODS,工程师们可以使用敏感protocal buffer的数据集,并在数据集owner违反策略时发出警告。
- 重新找到数据集。工程师们在工作中会生成很多“实验的”数据集。但是,当他们想分享这些数据集或者继续在其之上工作的时候,通常会忘记数据集路径。使用简单的关键字可以轻松找到这些数据集。
- 理解老代码。很难找到老代码的最新文档。GOODS的provenance图可以用来跟踪老代码之前的执行,包括其输入、输出数据集,从而提供理解逻辑的线索。
- 数据集标签。数据集概览页面是一站式记录数据集信息的地方。用户可以建立标签,便于访问和分享跟其他用户。
- 注释数据集。GOODS目录提供了数据集注释的一个枢纽,可以跨团队分享。例如,团队可以用不同的隐私等级打标他们的数据集,警告工程师们注意数据集的使用,也便于策略检查。
值得注意的是,最后一个特性是由google内部另一个团队构建的。这个外部贡献帮助构建公司范围的数据集管理工具生态系统。
在开发GOODS时,我们和google内部团队开过几次会议讨论他们在数据集管理上的痛点。我们很快意识到,除了搜索,还需要一整套数据集管理工具,包括监控数据集健康的大盘、自动化数据集测试、了解数据集之间差异的工具。我们的可扩展设计可以使我们以较少的工作量来支持这些场景。此外,一些工具可以将其他场景中的使用情况作为额外元数据来增加目录。例如,数据大盘中显示监控的数据集明显是重要的,因此可以提高它们的排名。
(2)使用特定域的信号排序。正如第2节所描述的,相比于其他领域(比如web排序)的排序问题,数据集排序问题有独特的特征。例如,数据集之间的provenance关系提供了很强的域特定的排序信号。特别地,团队通常生成“主”数据集的非规范版本,来进行主数据集的不同类型的分析。这些非规范数据集与主数据集有相通的搜索关键字,但是显然主数据集在通用查询和元数据提取上应该具有更高的排名。另一个例子来自于跨团队的provenance关系,一个团队的数据集经过处理并在另一个团队创建了数据集。在这种情况下,我们提升外部团队使用的输入数据集的重要性。输出数据集也很重要,因为可以看作其他团队数据集的来源。
识别provenance关系类型是个有趣的科研问题,尤其是在事后型元数据情况下。可以使用几个信号,包括数据集内容相似性、已知的provenance关系、来源于群众的信息比如owner提供的描述。一旦识别了关系类型,就必须在排名上下文中对它们进行推理,这本身涉及一些列不同挑战。
(3)预测和处理不寻常的数据集。目录中有大量数据集,早起遇到了很多预期外的情况。一些通过专用代码一次性解决了,而另一些需要系统层面的重设计。例如3.2节中提到的抽象机制,采用特殊逻辑提取非传统的日期(例如:“05Jan2015”)和版本。处理某些数据集会由于代码外的三方库问题导致分析器崩溃,需要4.3节中描述的隔离机制。起初采用点对点的解决方案,后来再通用化。
(4)按需导出数据。GOODS目录的存储介质是kv存储,搜索服务基于传统的反向索引。然后这些都不适合可视化和provenance图的复杂路径查询。为了支持这些使用场景,每天将数据导出为主体-谓词-对象三元组。然后将这些三元组倒入到一个图系统中,并提供一个更易于可视化的API。对于一些不支持的需要更强大的查询处理能力的场景,最简单的做法是导出目录数据到合适的专门的引擎中。
(5)确保可恢复性。提取几十亿数据集的元数据成本很高。在稳定状态下,在一天内处理当天有价值的数据集。丢失或损坏目录中的大部分,需要花费数周来恢复,除非投入大量额外的计算资源来恢复。此外,在一些严重的数据丢失之后,甚至可能很难重计算一些元数据。例如,一些临时文件连接了provenance图的输入和输出数据集,如果丢失了从临时文件推导出的provenance数据,很难再恢复它。
为了确保可恢复性,配置Bigtable保留几天滚动的快照窗口,也建立了GOODS特有的恢复机制。具体地,为了防止数据丢失,在一个单独的目录中添加专门的快照高值数据集(用户通过注释显示表达感兴趣)。另一个进程复制目录中概率页面相关的子集,这样即使主目录下线,服务仍然可用。此外,使用将GOODS的数据集监控服务应用在目录上,确保尽早发现数据损坏或删除。基于多次修复目录的经验,得到这种结合的方法。
7 相关工作
IBM的data-lake management system,Data wrangling: The challenging journey from the wild to the lake
data-lake offerings in cloud services,Azure data lake. https://azure.microsoft.com/en-us/solutions/data-lake/.
GOODS与前两者的主要区别在于lake的规模和事后型元数据推导。也是一种dataspace理念,From databases to dataspaces: A new abstraction for information management.
databub,协作版本管理系统,DataHub: Collaborative data science & dataset version management at scale. Collaborative data analytics with DataHub. Principles of dataset versioning: Exploring the recreation/storage tradeoff.
数据管理系统CKAN、Quandl、Microsoft Azure Marketplace。
上述的都需要数据集owner主动贡献数据集到系统中并注释元数据。
8 未来工作
遗留的挑战:
1 距离完全理解如何排名数据集、如何识别数据集重要性尚远。
2 填补缺失的元数据,整合数据集、代码、团队信息。
3 数据集语义。
4 存储系统创建数据集的时候向GOODS注册元数据。虽然违背事后型,但是可以结合这两种方法。
5 在数据驱动公司推动数据文化,像代码规则一样有一系列数据规则。