下载本文的PDF版本 PDF

为权威数据带来任意计算

单一存储系统可以满足许多不同的用例。


Mark Cavage 和 David Pacheco,Joyent


虽然大数据这个术语已经足够模糊,以至于失去了很多意义,但今天的存储系统增长速度比以往任何时候都快,管理的数据也比以往任何时候都多。消费设备生成大量的照片、视频和其他大型数字资产。机器在数据生成方面正迅速赶上人类,这得益于系统日志和指标的大量记录,以及视频捕获和基因组测序等应用。大型数据集现在司空见惯,人们越来越希望对这些数据进行复杂的分析。在本文中,大数据指的是足够大的数据集,可以显著受益于跨系统集群的并行计算,而高效地编排计算本身就是一个相当大的挑战。

操作大数据的首要问题是维护基础设施以持久存储它,并确保其可用于计算,其范围可能从分析查询访问到通过 HTTP 的直接访问。虽然存储问题没有通用的解决方案,但管理记录存储系统(即,托管必须永不丢失的数据主副本的系统)通常属于企业存储解决方案的范畴,例如 SAN(存储区域网络)。然而,这些解决方案通常不提供 WAN(广域网)访问,并且它们通常需要额外的基础设施来从任意客户端摄取数据和向任意客户端导出数据;这很难随着数据足迹扩展。

一旦建立了记录系统,对大型数据集的计算通常需要 ETL(提取-转换-加载)步骤,将其加载到实际可以执行计算的系统中。即使原始数据格式足够,基于 NAS(网络附加存储)和 SAN 的系统也不支持就地计算,而是需要通过网络进行昂贵的复制——对于 PB 级数据集来说,这基本上是不可行的。

最后,特殊用途和通用计算接口之间存在重要区别,这意味着类似 SQL 的接口和 Unix shell 之间的抽象差异。虽然前者功能强大,并且是许多类型分析的适当选择,但它不适用于许多临时任务,例如视频转码、压缩或处理非结构化或半结构化数据的其他操作。现有的分布式计算系统通常要求用户理解复杂的框架(放弃他们熟悉的工具)或使用特殊用途的接口(这通常会在每次需要新接口时引发上述数据复制问题)。

本文的目标是描述一种通用的分布式存储系统,该系统支持对静态数据进行任意计算。本文首先详细介绍了约束条件,这些约束条件指导了大部分设计;然后描述了一个名为 Manta 的实现,可以将其视为参考实现,但肯定不是构建此类系统的唯一方法;最后介绍了如何使用 Manta 解决几个常见的大数据问题。

约束

设计约束可以大致分为存储抽象(用户如何存储和获取数据)、编程模型(用户如何在数据上表达原位计算)以及系统如何提供持久本地存储(系统如何在磁盘上持久存储位)。

存储抽象:对象存储

构建可扩展的系统意味着确保可以通过添加更多硬件来增加容量。在不停机且不增加单个组件故障影响的情况下进行扩展需要水平扩展,但是 Posix 文件系统和块存储语义很难水平扩展。更新可能非常小(文件系统中的一个字节;块设备中的几千字节的块)且频繁。即使不考虑多个客户端,操作延迟和持久性之间的权衡也很困难:客户端在传输到服务器之前允许缓冲的数据越多,操作执行速度可能越快,但在发生崩溃时风险越高。对于在同一文件上操作的多个客户端,操作延迟和一致性之间也存在类似的权衡。

对象存储提供了一种更受约束的模型,该模型更易于扩展。对象存储类似于文件系统,因为它具有带有对象(数据 blob)和目录(分层集合)的全局命名空间,但是对象存储不支持部分更新。用户可以创建新对象并删除旧对象,但是他们无法在不完全替换现有对象的情况下更新现有对象的内容。对于强一致性,对象存储实现只需要确保命名空间是事务性的;对象本身是不可变的,因此易于复制和缓存。

编程模型

分布式计算。 MapReduce 编程模型3构成了多个分布式计算系统的基础——这是有充分理由的。从根本上讲,MapReduce 将分布式计算划分为转换各个数据片段的部分(map 操作)和一次操作大量数据的部分(reduce 操作)。这两种类型的操作都是输入数据的转换,产生一组输出数据。原始数据是不可变的,因此没有副作用。这对于分布式计算有几个很好的特性

• 用户只需要表达特定于他们问题的计算部分,将数据流的问题留给系统本身。

• Map 操作是完全可并行化的。在实践中,您可以添加硬件资源以几乎线性地增加并行性。

• 在发生故障时,可以重试 map 和 reduce 操作。故障后没有复杂的状态需要回滚——只有输出需要忽略。即使在网络分区的情况下,当系统可能不知道操作是否失败时,它也可以多次执行操作,只要它忽略除一个副本以外的所有结果。

本地计算。 MapReduce 是一个有用的分布式计算模型,但问题仍然存在,用户如何指定 map 或 reduce 操作实际执行什么?首选是用户已经熟悉的界面,可以利用大量现有软件,并尽可能少地施加约束。考虑到这些优先级,Unix CLI(命令行界面)中存在一个令人惊讶的(如果很明显的)解决方案。

CLI 的核心为任意计算提供了简单的界面,并带有一些额外的原语

• 用于读取和写入输入和输出流的文件描述符,以及围绕主输入、主输出和用于报告其他消息的流的约定。

• 管道,将一个程序的输出连接到另一个程序的输入,有效地组合成一个新程序。

这种方法鼓励创建许多小工具,这些小工具可以很好地组合在一起以运行更复杂的计算。这也许最好地说明了 C 的“编程珠玑”系列中的一个名为“文学编程”的示例1,其中提出了以下问题

“给定一个文本文件和一个整数 k,打印文件中 k 个最常见的单词(及其出现次数),并按频率递减排序。”

文章描述了两种解决方案:第一种是自定义解决方案,其简洁的演示(带有解释)跨越七页;第二种是“当场编写”的 shell 单行命令,“第一次尝试就成功了”,使用了六阶段管道,使用了tr, sort, uniq,sed。代码和解释总共约占四分之一页。这怎么强调都不过分:基于 shell 的解决方案对于作者构建以及后续读者理解、修改和与其他程序组合来说要快得多。

具有讽刺意味的是,原始论文第 2.1 节中的规范 MapReduce 示例提出了一个非常相似的问题:“计算大型文档集合中每个单词的出现次数。” 将 Unix 接口扩展到基于 MapReduce 的分布式计算模型可以获得很多好处,但存在一些挑战。

• 编程接口。 MapReduce 通常对键或元组进行操作。虽然许多 Unix 工具确实对字段分隔的记录进行操作,但这只是一种约定。MapReduce 可以应用于存储系统中的对象,而不是应用于大型统一的键集。为了以熟悉的方式处理对象,可以将它们作为只读文件暴露在 stdin(标准输入)以及文件系统上。

当 MapReduce 计算表示为 Unix 管道时,程序必须能够像在任何其他 Unix 系统上一样与系统交互,在隔离的操作系统容器中拥有自由。它们必须拥有自己的进程命名空间、文件系统、网络堆栈以及程序可见的任何其他硬件和软件资源。

• 多租户。 生产存储系统必须支持单个用户和多个用户的并发计算,并且不应要求手动安排系统上的时间以确保服务质量。并发计算应共享所有可用资源(可能受管理策略的约束),并且应允许用户像每个用户都是系统上的唯一用户一样进行编程。他们不得相互查看或干扰。

• 副作用。 由于 Unix 程序可以读取和写入本地文件系统、fork 进程、建立网络连接等等,因此它们确实有副作用。可以通过以下方式处理此问题:无论程序在运行时做什么,它与分布式计算交互的唯一方式是通过其输入和输出流。当程序完成时,任何会影响后续程序环境的内容都必须回滚,包括文件系统更改、创建的进程等。这是通过基于操作系统的虚拟化实现的,使用提供隔离和资源控制的容器以及支持高效回滚的文件系统(稍后讨论)。

持久本地存储

直接将对象作为文件暴露给用户程序的愿望导致了本文描述的系统与现有对象存储系统之间最显着的差异,即每个对象的全部内容必须存储在单个系统上。对象可以存储在多个服务器上以提高可用性和持久性,但为了支持我们的编程模型,整个内容必须存在于每个系统上。这反过来要求本地文件系统持久地存储数据。虽然这听起来很简单,但物理介质的现实意味着本地存储系统需要许多功能来有效地管理存储并确保数据持久性。

数据块外部的校验和。 据信已写入磁盘的数据在稍后读回时可能会以多种方式损坏,包括磁盘控制器、电缆或驱动器固件中的损坏;直接意外写入磁盘;环境影响导致正确写入后磁盘上的位发生更改(位衰减);固件中的错误导致写入丢失(幻影写入)或错误定向的写入;等等。虽然块校验和可以防止位衰减,但它们无法检测到写入错误位置的格式良好的块(可能发生在意外覆盖、错误定向的写入或幻影写入中)。文件系统必须校验数据,并将校验和与数据块分开存储,以便检测到给定块不仅有效,而且还表示该块中期望的数据。

双奇偶校验(或更好)的基于软件的 RAID。 为了以合理的成本为对象的每个副本获得适当的持久性,每个节点的存储本身应该是冗余的。单个 spindle 故障不应导致该服务器上对象的可用性或持久性降低。为了重建错误的数据块副本(使用刚刚描述的文件系统管理的校验和检测到),文件系统必须参与数据重建过程,这意味着基于软件的 RAID(独立磁盘冗余阵列)。

写时复制。 写时复制文件系统支持 O(1) 时间点快照和高效回滚到以前的快照。这是回滚用户程序所做的更改的关键功能,以便其他用户的程序可以在同一容器中稍后运行。

池化存储。 存储服务器必须为用户程序提供临时暂存空间。隔离要求用户获得自己的文件系统,而支持具有灵活资源控制的多个租户的愿望要求动态调整这些文件系统的大小。因此,文件系统不应直接布局在磁盘上,而应从存储池中灵活分配。

单独的意向日志。 大型存储系统的经济性要求使用传统磁盘作为主存储,但使用 SSD(固态驱动器)作为意向日志设备可以显着提高写入性能。流式写入可以利用大量 spindle 的可观吞吐量,而同步写入(通常较小)可以提交到 SSD 并向客户端确认,然后再写入 spindle。

其他设计目标

除了这些硬性要求之外,系统还需要几个属性来实现一致性、可用性和持久性。

CAP

CAP 定理2 规定,在面对网络分区(这将始终发生)时,数据服务必须在一致性可用性之间做出选择。近年来,分布式系统的趋势已向可用性倾斜,虽然可用性(最终一致性)通常是应用程序的正确选择,但为通用存储系统选择弱一致性模型意味着构建在该存储系统之上的任何应用程序都永远不可能具有强一致性。相反,如果底层存储系统是强一致性的,则应用程序始终可以采取措施通过实现异步写入(暂存)和缓存读取来选择可用性。由于这些原因,记录存储系统必须是强一致性的。

可用性和故障模式

选择强一致性并不意味着系统应放弃高可用性。系统应在任何单个组件发生故障时幸存下来,包括单个服务实例、物理服务器、机架或整个数据中心。每个组件都必须跨三个或更多数据中心进行复制和横向扩展。在经典的 2F+1 故障模型6中,系统应支持在单个数据中心离线的情况下继续运行。(可能会有短暂的重收敛时间,在此期间写入将被拒绝,但之后系统将继续运行而不会降级。)

数据访问和持久性

数据应通过 REST(具象状态传输)API 暴露。当系统返回HTTP/1.1 200 OK在写入之后,副本必须存在于两台物理服务器上,并且关于实际数据的元数据的副本必须存在于两台物理服务器上。这些副本还必须跨越数据中心。如前所述,每个物理副本都必须能够抵抗单个驱动器故障。

实现

Manta(图 1)是 Joyent 对刚刚描述的设计目标的存储系统的实现。虽然架构主要直接来自所描述的约束和目标,但该实现处理了几个重要的细节。

Bringing arbitrary compute to authoritative data: Manta data path

存储

在高层次上,用户与可扩展的 Web API 交互,该 API 是无状态且负载均衡的。关于对象的元数据存储在应用程序分区的数据库层中,对象本身同步写入到集群中的两个或多个存储节点。

前端

为了保持高可用性,大多数 Manta 组件都是无状态的。前端由一组使用 HAProxy 的负载均衡器和用 Node.js 编写的自定义 API 服务器组成。用户的 TLS(传输层安全)会话在负载均衡器处终止,HTTP 请求由 API 服务器处理。还有一个由 Redis 支持的只读身份验证/授权缓存。每个数据中心至少有每个组件的一个实例,负载均衡器本身的负载均衡使用轮询 DNS 粗略地处理。由于所有这些组件都是无状态的,因此可以配置新实例以获得额外的可用性或容量。

写入请求在逻辑上分解为以下步骤

1. 确保父路径(目录)存在。

2. 选择一组 N 个存储节点来存储数据。默认情况下,N = 2,但用户可以指定更多或更少的副本。每个副本都具有高度持久性(如已描述),但副本存储在单独的数据中心中,因此拥有更多副本会提高可用性。

3. 同步将数据流式传输到选定的存储节点。

4. 记录持久元数据(按对象的用户可见名称索引),指示哪些存储节点包含数据。

读取请求分解为类似的一系列步骤

1. 查找请求对象的元数据,其中包括具有对象副本的存储节点集。

2. 并行联系每个托管数据副本的存储节点。

3. 从第一个响应的节点流式传输数据。

删除和覆盖由后台垃圾回收系统处理。当删除(或覆盖)请求进入时,Manta 会将事件记录在持久事务日志中。垃圾回收系统稍后确定元数据层不再引用哪些对象,并回收存储节点上相应的空间。

元数据

根据设计约束,对象元数据必须持久存储和事务性修改,但仍然具有高可用性和水平可扩展性。Manta 将元数据存储和访问分为三个组件

1. 数据存储和复制引擎。

2. 面向 (1) 的键/值接口。

3. 用于 (2) 的分片层,以支持水平可扩展性。

数据存储和复制。由于元数据是高度结构化的,因此经典的 B 树表示足以支持事务性更新和对索引属性的快速查询。为了保持可用性,系统还必须支持同步复制到辅助实例。在辩论是构建我们自己的解决方案还是使用现成的解决方案之后,我们决定使用 PostgreSQL,其存储子系统已被证明是可靠的,并且其复制系统满足这些要求。PostgreSQL 缺少的是一个内置系统,用于使用领导者选举来确保自动故障转移。我们编写了利用共识层 (Zookeeper) 来管理副本菊花链的软件。当 PostgreSQL 对等方崩溃或退出集群时,直接位于其后面的对等方将在拓扑中升级

Master -> 同步对等方 -> 异步 -> 异步 -> ...

此外,拓扑由领导者记录在一个众所周知的位置,并且键/值层(所有客户端都通过它访问数据库)知道如何使用此信息。

Moray:键/值接口。 在 PostgreSQL 之上是一个名为 Moray 的轻量级键/值接口,它提供三个功能

• 了解底层的数据库复制拓扑,以确保写入始终定向到 PostgreSQL 主服务器。

• 一个简单的接口,没有显式事务,支持乐观并发控制。

• 使用 LDAP(轻型目录访问协议)搜索过滤器的足够好的查询接口。

为了程序员的简单性,一个PUT/GET/DELETE范例使键/值接口抽象为bucketskeysobjects,其中 objects 始终是 JSON(JavaScript 对象表示法)文档。为每个 bucket 定义索引规则,以便搜索请求能够查找与在 JSON 对象上评估的过滤器匹配的对象。简单的查找直接通过 bucket/key 对完成。

以下 Node.js 代码演示了 API 和语义

// 创建一个名为 "user_directory" 的 bucket,并确保任何
// 写入其中的对象都会检查属性 "id"、"name"、
// 和 "email";如果存在任何属性,则在这些属性上维护索引以用于
// 未来的搜索请求
moray.putBucket('user_directory', {
    index: {
        id: 'number',
        name: 'string',
        email: 'string'
    }
});

// 将示例用户对象写入 "user_directory",键为
// "mcavage",值为同时具有索引字段和非索引字段的
// 字段
moray.putObject('user_directory', 'mcavage', {
    id: 123,
    name: 'Mark Cavage',
    email: '[email protected]',
    department: "engineering"
});

// 搜索任何具有电子邮件地址 "@joyent.com" 的对象
var res = moray.search('user_directory', '(email=*@joyent.com)');
res.on('record', function (obj) {
    console.log('找到记录: %s', JSON.stringify(obj));
});

该系统是 Manta 中其他组件用于存储状态的唯一接口。关于用户对象、存储节点利用率和计算作业状态的所有元数据都通过 Moray 存储在 PostgreSQL 中。

Electric Moray:键/值接口。 至少三个复制的 PostgreSQL 实例加上一个或多个 Moray 实例的集合称为shard。在任何给定时间,shard 中只有一个 PostgreSQL 实例用于读取和写入,并且 Moray 实例将所有键值操作定向到该实例。持久性和可用性在每个 shard 中提供,但为了水平扩展(即,为了增加此系统的存储足迹或读/写容量,使其超出单个实例可以提供的范围),Manta 使用多个 shard 并在它们之间使用一致的哈希方案5执行应用程序级分区。

回想一下,面向用户的 API 由目录和对象组成,它们的行为在很大程度上类似于 Posix 目录和文件。与 Posix 文件系统一样,为了写入对象,所有父目录都必须存在。以下 CLI 命令应该看起来很熟悉

    $ mmkdir /mark/stor/foo/
    $ echo "Hello, " | mput /mark/stor/foo/bar.txt

为了确定哪个 shard 保存给定对象的元数据,Manta 对对象的目录名称执行一致的哈希操作。例如,给定/mark/stor/foo/bar.txt,Manta 哈希字符串/mark/stor/foo。目录中所有对象的元数据都存在于单个 shard 上。

管理对象名称和 shard 之间映射的系统称为 Electric Moray。它提供与 Moray 相同的接口(并最终将所有请求定向到 Moray 实例),但处理将请求路由到正确的 shard。

存储服务器

存储节点主要负责维护对象单个副本的持久性,并提供隔离的计算容器,在其中运行用户程序。对于存储,这些节点通过本地磁盘提供直接的 HTTP 接口,本地磁盘由 ZFS 文件系统管理。对象使用随机 (UUIDv4) 名称写入。在写入时,仅当fsync(2)已完成并且已知数据安全地位于磁盘上时,才会将成功的状态代码返回给调用操作。

Manta 利用 ZFS 来解决本地持久性问题。这满足了之前描述的所有约束,包括用于检测所有形式的块级损坏的校验和、自动检测和修复此类损坏(在读取数据时)的软件 RAID 以及池化存储以支持动态调整文件系统大小以满足用户对额外暂存空间的需求。三重奇偶校验软件 RAID 实现可以在面对多达三个并发磁盘故障的情况下维持稳态运行(包括写入),但系统配置为双奇偶校验以获得更好的成本效率。

计算

用户通过提交作业在 Manta 中运行计算。为了支持类似 MapReduce 的模型,作业由一个或多个阶段组成,每个阶段都是 map 阶段或 reduce 阶段。Map 阶段分为任务——每个输入对象一个任务。用户可以为给定的 reduce 阶段指定他们想要的 reduce 任务的数量。

一个重要的设计目标是作业应该能够对任意数量的对象进行操作,具有任意数量的任务,产生任意数量的错误或输出,这意味着数量应该随着系统资源扩展,而不是由于算法原因而受到限制。为了实现这一点,列出输入、输出和错误的接口必然是流式和分页的,而不是在单个请求中提供完整的列表。在内部,输入、输出、错误和任务以组为单位进行操作,并且永远不需要将它们全部(或它们的任何特定组)存储在内存中。

作为一个具体的例子,这是一个仅 map 的作业,它在本地文件/login)中命名的一组 Apache 格式的请求日志中搜索给定 URL 的实例(inputs.txt。用户将从本地系统运行此命令

    $ mjob create -o -m 'grep -w /login' < inputs.txt
    added 240 inputs to 4e55b83b-a04b-c25c-9169-d4b4641d915d
    10.10.0.34 - - [14/Apr/2014:11:07:48 +1000] "GET /login HTTP/1.0" 200 6433 "-" "nodejs"
    ...

mjob create客户端命令运行新的 Manta 作业。-m选项指定由以下脚本定义的 map 阶段。-o选项使命令等待直到作业完成,然后打印作业输出的内容。这是在 shell 中本地运行命令的最接近的类比,除了它并行运行并且grep实际上在存储服务器上运行。inputs.txt文件包含要在其上运行作业的对象列表。由于这是一个 map 作业,系统将运行grep -w /login对于 inputs.txt 中的每个对象,自动尽可能地并行化。

在 API 中,作业看起来像这样

    {
        "id": "ea5290f9-4f1b-e52c-9c47-ffe9fc6f7905",
        "state": "running",
        "inputDone": true,
        "stats": {
            "errors": 0,
            "outputs": 0,
            "retries": 0,
            "tasks": 240,
            "tasksDone": 0
        },
        "phases": [ {
            "exec": "grep -w /login",
            "type": "map"
        } ],
        ...
    }

当它完成时,state将会完成,并且tasksDone将等于tasks.

执行此作业分为两个部分:作业的分布式编排和用户的grep脚本在每个对象上的执行。

作业编排。 当用户运行mjob create,Manta Web 服务接收客户端请求以创建作业,向作业添加 240 个输入,并指示将不再有更多作业输入。该作业被分配给特定的作业主管,该主管负责协调作业的分布式执行。当输入被添加到作业时,主管将每个对象的名称解析为内部 uuid(通用唯一标识符),该 uuid 标识对象并检查用户是否被允许访问该对象。假设用户被授权访问该对象,主管将在集群中找到该对象的所有副本,选择一个副本,并向存储该副本的服务器上运行的代理发出任务。对于每个输入对象,都会重复此过程,从而在集群中分配工作负载。

存储服务器上的代理接受任务并在隔离的计算容器中运行用户的脚本。它记录执行脚本时发出的任何输出。当任务完成运行时,代理将其标记为已完成。(此过程将在稍后更详细地描述。)

主管提交已完成的任务,将其输出标记为最终作业输出。当没有更多未处理的输入且没有未提交的任务时,主管声明作业完成。

如果任务失败,它将被重试几次,最好在不同的服务器上重试。如果持续失败,则会产生错误。

多阶段 map 作业类似,不同之处在于每个第一阶段 map 任务的输出将成为新的第二阶段 map 任务的输入,并且只有第二阶段的输出才会成为作业的输出。

Reduce 任务。 Reducer 作为 mapper 运行,不同之处在于 reducer 的输入在上一阶段完成之前是完全未知的。此外,reducer 可以读取任意数量的输入,因此输入本身作为单独的记录分派,并且在 reducer 可以完成之前必须发出单独的输入结束信号。

假设您想要修改之前的示例,以报告每个 IP 地址请求/sample.txt的次数。您可以更改 map 阶段以仅发出 IP 地址列,并让 reduce 阶段计算不同值的数量

    $ mjob create -o \
        -m 'grep -w /sample.txt | cut -d" " -f 1' \
        -r 'sort | uniq -c' < inputs.txt
    added 240 inputs to 4e55b83b-a04b-c25c-9169-d4b4641d915d
        135 10.10.0.34
          5 10.10.0.37
         23 10.10.0.33
    ...

请注意,此问题的两个部分正是您在单个系统上使用 shell 解决相同问题时会运行的内容。分布式版本实际上可以将 reducer 与 mapper 一起应用,并用一个awk脚本替换此 reducer,该脚本对每列的值求和,但这种性能优化通常不是必要的。

对于这个两阶段作业,第一阶段的工作方式如上所述。对于 reduce 阶段,当作业创建时,会向集群中某处的代理发出单个 reduce 任务。当 map 任务完成时,它们的输出(本身是临时的 Manta 对象)被标记为 reducer 的输入。Reducer 发出单个输出对象,然后被标记为已完成。和以前一样,主管提交结果,生成作业的唯一输出对象,并且作业被标记为已完成。

本地执行。每个存储服务器上的代理维护一组固定的计算区域,用户脚本可以在其中运行。当 map 任务到达时,代理在本地文件系统中找到表示输入对象的文件,找到一个空闲的计算区域,将对象映射到本地文件系统中,并运行用户的脚本,将 stdin 从输入文件重定向,并将 stdout 重定向到本地文件。当脚本退出时,假设它成功,输出文件将作为对象保存在对象存储中,记录为任务的输出,并且任务被标记为已完成。如果同一作业有更多工作要做,代理可以选择在同一计算区域中运行它,而无需在第一个任务之后进行清理。当没有更多工作要做,或者代理决定将计算区域重新用于另一个作业时,计算区域将停止,文件系统将回滚到其原始状态,并且该区域将再次启动以运行下一个任务。由于计算区域彼此隔离,并且在作业之间完全重启和回滚,因此用户的作业无法看到或干扰系统中运行的其他作业。

计算框架内的内部通信。系统使用分片、复制的数据库组件 (Moray/PostgreSQL shards) 来管理作业状态。有用于作业、作业输入、任务、任务输入(用于 reduce 任务)、任务输出和错误的 bucket。主管和代理轮询适用于它们的新记录。例如,主管轮询分配给它们但已完成但未提交的任务,代理轮询分配给它们但已分派但未接受的任务。当作业完成时,输入、输出和错误的列表被归档到 Manta 对象中,并且相应的数据库记录被删除,以将数据库限制为仅当前工作状态。基于轮询的方法具有明显的性能缺点,但实现相对简单,并且可以在不更改基本设计的情况下大大提高性能(例如,通过减少轮询间隔和使用推送通知来触发立即轮询)。

虽然这种机制已被证明是可靠的,但它也是系统中从作业输入到相应输出的大部分延迟(对于 500 毫秒的用户程序,为两到三秒)的原因。工作负载还在 PostgreSQL 层中引发了病态性能问题,导致 brownout 情况,在这种情况下,通常需要几秒钟才能完成的作业可能需要几分钟才能完成。现在它们已被理解,这些病态通常可以避免或减轻。

示例:带宽和计算计量的日志分析

Manta 本身提供了一个大数据系统常见用例的示例,即持续日志分析。使用 MapReduce 作业对内部日志文件进行操作,从而计算每个用户的带宽和计算使用指标,这些日志文件本身每小时都会推送到 Manta 中。这些作业将内部日志文件作为输入,解析日志条目,计算带宽和使用的计算时间等指标,并生成每个用户、每小时的访问日志,以及也用于计费的汇总使用情况报告。reduce 过程分为两个阶段,以提高并行性。在构建 Manta 时牢记这一关键用例极大地影响了系统的设计。

示例:客户群组留存率

一位客户广泛介绍了构建在 Manta 之上的数据分析系统。4 它使用 rsyslog 每天从 40 多个前端 Web 服务器记录用户操作。这些日志,代表每天约 500 万到 2000 万个事件,以 ASCII 码管道分隔的文件形式,总计约 1 GB,上传到 Manta。典型的业务问题包括“在给定日期,有多少唯一用户使用 iPad 执行了给定操作?”以及“有多少四周前注册的人仍然活跃?” 由于其简单的格式,这些问题可以通过 5 到 10 行的脚本使用grep(1), awk(1), sort(1), uniq(1), comm(1),wc(1)来回答。结果通常在几秒钟到一分钟内可用。

示例:视频分析和转码

许多客户已使用 Manta 转码视频。我们也在内部为一个分析马里奥赛车 64 游戏屏幕截图的副项目 ( http://kartlytics.com/ ) 做这件事。分析视频的程序构建在 FFmpeg 工具套件和用于视频处理的库之上。该应用程序是一个典型的 MapReduce 作业,它映射视频文件并生成描述视频中发生的事情的 JSON 文件。将生成的文件组合起来以生成单个摘要文件,该文件在网站上显示所有结果。一个单独的仅 map 作业将原始的高质量视频文件转码为更小的、Web 质量的 WebM 文件。整个语料库在 10 分钟内完成分析(在 Manta 集群中完全并行化),而使用单个系统则需要一天的大部分时间,而无需修改分析软件本身。

结论

将记录存储系统与原位计算统一起来,并将无处不在的 Unix shell 环境适应于分布式计算,已使得许多不同的用例通过单个存储系统得到满足。这些用例包括传统的日志分析,以及资产托管(如内容交付网络)、图像处理(例如,在为 Web 托管的同一资产上)和视频转码。实现这一点需要非常规的设计选择,例如直接将对象存储为原始文件,而不是跨多个服务器进行擦除编码。虽然无疑还有其他方法可以构建这样的系统,但它们必然需要可靠的本地存储和成熟的基于操作系统的虚拟化。虽然以前的系统要么利用企业级网络存储(这被认为是不可行的,因为它需要移动数据才能在其上进行计算),要么构建自定义存储系统以解决原本不可靠或低效的本地存储,但使用基于 ZFS 的架构使我们能够专注于问题的分布式系统部分,并(我们认为)生成一个更通用的系统。

还有大量额外的工作要做,包括更丰富的访问控制、对更多开箱即用软件的支持以及改进的性能。但 Joyent 及其客户已经发现该系统对于分析业务和技术数据都很有价值。

参考文献

1. Bentley, J., Knuth, D., McIlroy, D. 1986. Programming Pearls: a literate program. Communications of the 29(6): 471-483; http://dl.acm.org/citation.cfm?id=315654.

2. Brewer, E. A. 2000. Toward robust distributed systems. Keynote speech. Nineteenth Symposium on Principles of Distributed Computing; http://www.cs.berkeley.edu/~brewer/cs262b-2004/PODC-keynote.pdf.

3. Dean, J., Ghemawat, S. 2004. MapReduce: simplified data processing on large clusters. Sixth Symposium on Operating System Design and Implementation; http://research.google.com/archive/mapreduce.html.

4. Gredeskoul, K. 2013. Using Manta to scale event-based data collection and analysis; http://www.slideshare.net/kigster/ss-26329742.

5. Karger, D., Lehman, E., Leighton, T., Panigrahy, R., Levine, M., Lewin, D. 1997. Consistent hashing and random trees: distributed caching protocols for relieving hot spots on the World Wide Web. Published in Proceedings of the 29th Annual Symposium on Theory of Computing: 654-663; http://dl.acm.org/citation.cfm?id=258660.

6. Lamport, L. 2006. Lower bounds for asynchronous consensus. Microsoft Research; http://research.microsoft.com/en-us/um/people/lamport/pubs/lower-bound.pdf.

另请参阅

Fontaine, T. J. 2013. 550 regression tests in 4 minutes with Joyent Manta; http://www.joyent.com/blog/550-regression-tests-in-4-minutes-with-joyent-manta.

Robbins, C. 2013. Keeping the npm registry awesome; http://blog.nodejs.org/2013/11/26/npm-post-mortem/.

喜欢它,讨厌它?请告诉我们

[email protected]

Mark Cavage 是 Joyent 的工程副总裁,负责监督公司核心技术的开发:Node.js、SmartOS、SmartDataCenter 和 Manta。他之前曾在 Amazon Web Services 担任高级软件工程师,领导身份和访问管理团队。

David Pacheco 是 Joyent 的一名软件工程师,主要从事 Manta 存储服务方面的工作。他还致力于 Joyent 的 SmartDataCenter 产品以及用于跟踪和调试生产中 Node.js 程序的工具。

© 2014 1542-7730/14/0600 $10.00

acmqueue

最初发表于 Queue vol. 12, no. 6
数字图书馆 中评论本文





更多相关文章

Qian Li, Peter Kraft - 事务和无服务器是天作之合
数据库支持的应用程序是无服务器计算令人兴奋的新领域。通过紧密集成应用程序执行和数据管理,事务性无服务器平台实现了许多在现有无服务器平台或基于服务器的部署中不可能实现的新功能。


Pat Helland - 任何其他名称的身份
新兴的系统和协议都在收紧和放松我们对身份的概念,这很好!它们使完成工作变得更容易。REST、物联网、大数据和机器学习都围绕着有意保持灵活,有时甚至是模糊的身份概念。身份概念是我们分布式系统基本机制的基础,包括互换性、幂等性和不变性。


Raymond Blum, Betsy Beyer - 实现数字永恒
当今的信息时代正在为世界所依赖的数据创造新的用途和新的管理方式。世界正在从熟悉的物理人工制品转向更接近其本质信息的新的表示方式。我们需要流程来确保知识的完整性和可访问性,以保证历史将被知晓和真实。


Graham Cormode - 数据草图
您是否曾经感到被源源不断的信息淹没?似乎大量的新电子邮件和短信需要持续关注,还有电话要接听、文章要阅读,还有敲门声要应答。将这些碎片拼凑在一起以跟踪重要内容可能是一个真正的挑战。为了应对这一挑战,流数据处理模型越来越受欢迎。其目的不再是捕获、存储和索引每一分钟的事件,而是快速处理每个观察结果,以便创建当前状态的摘要。





© 保留所有权利。

© . All rights reserved.