您的位置:首页 > 其它

Spark RDDs(弹性分布式数据集):为内存中的集群计算设计的容错抽象

2014-03-29 16:44 267 查看
本文是阅读《Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing》过程中,抽了局部一些关注点翻译出来的文章,没有将全文都翻译。希望这些碎片化甚至不通顺的记录,可以帮助读者取代阅读原论文。

论文地址http://www.cs.berkeley.edu/~matei/papers/2012/nsdi_spark.pdf

第一节

主要介绍了现有的集群计算框架存在的问题,然后介绍了RDD的几个设计亮点,对比了Spark的不同之处和优势点

解决现存迭代式算法和交互式数据挖掘俩问题 iterative algorithms and interactive data mining tools.

严格的内存分享,粗粒度而非细粒度 based on coarsegrained transformations rather than fine-grained updates to shared state

Dryad 和 MR 是现有的集群计算框架 他们能分布式 能容错 但是没能利用好分布式内存

导致他们在多次利用中间数据的计算上不够效率

有很多迭代式机器学习和图算法 PageRank K-means LR

还有一点 就是帮助交互式数据挖掘

现有的解决计算之间(如两个MR job下面)重用数据的方法 是存到一个外部分布式文件系统里

这带来了很大的间接消耗在 数据备份,磁盘IO,以及序列化 而这些主宰了执行时间

已经出现的针对这一问题的特殊框架:Pregel 迭代式图计算 把中间数据存内存 HaLoop提供迭代MR接口

但他们只针对一部分特殊计算组件,对数据分享做的很含蓄,没有提供更抽象,通用的重用

RDD的一个原文定义:

RDDs are fault-tolerant, parallel data structures that let users explicitly persist intermediate results in memory, control their partitioning to optimize data placement, and manipulate them using a rich set of operators.

第二节

介绍RDD

RDD是只读,分区记录的集合

transformations:从现有的存储文件生成RDD或由别的RDD生成新RDD

RDD不用一直物化,他充分知道自己是怎么被计算出来的,他的lineage血缘

persistence and partitioning 是RDD另两个方面

持久化指用户可以指定他们要哪个RDD,存储策略是什么

分区指用户可以决定key来让RDD分区在机器之间,这对于 位置优化(placement optimizations)很有用

Spark编程API,对RDD进行的actions,如:count(),collect(),save()

且是懒式的,所以可以管道式变换 (pipeline transformations)

用户可以持久化RDD,Spark默认是放内存里,RAM不够大就放磁盘上

用户可以指定持久化策略,比如只存硬盘上或在不同机器上复制,设置flag,最后用户可以设定优先级,哪个内存里的数据先放到硬盘上

RDD与分布式内存(DSM)的对比:



DSM是一个很通用的抽象,他的普遍性导致了比较难实现容错和高效

RDD不适合大块的写,但是更高效和容错,且没有检查的开销,因为可以借助血缘恢复,且丢失的分区在重新计算的时候,也是在不同节点上并行的,比回滚整个程序快很多

对于大块数据的操作,RDD会调度到本地数据执行,提高效率。数据太大就存硬盘。

RDD不支持的场景:
RDD适合批处理,具体是对数据集里所有元素进行同一个操作,这样他执行转换和恢复就很快,原因是不记录大量log

不适合异步细粒度更新,比如web引用的存储和增量web爬虫


第三节

Spark编程接口

选用Scala是因为他的简明(交互式)和高效(静态类型),而RDD并没有函数式编程的需求

RDD是静态类型对象

join适合key-value的RDD



拿逻辑回归里的梯度下降和PageRank,介绍了下迭代式计算的方式和RDD上的写法

第四节

RDD的表示

如何表示RDD之间的依赖:窄依赖,一个父RDD最多被一个子RDD用(map);宽依赖,多个子RDD(join)

窄依赖:在一个集群节点上管道式执行,计算所有父分区。如map->filter;错误恢复很高效

宽依赖:要求所有父分区可用,在所有节点上进行类mapreduce操作




第五节

实现

每个Spark程序在Mesos上是一个独立的应用,有自己的驱动,worker以及Mesos分配的共享资源

下面讲几个实现中的有趣部分:任务调度,允许交互式的Spark解释器,内存管理,检查支持(checkpointing)

任务调度

Spark的任务调度类似Dryad

每当用户进行一次action,首先会检查RDD的血缘图,建立一个stages的DAG,按stage来执行这次action

每个stage都尽量包括尽可能多的管道式变换的窄依赖,stages之间的界限,就是宽依赖的洗操作,或者任何已经准备好了的分区。调度器就一个个执行这些stage,直到目标RDD生成出来



调度器布置任务是基于数据本地化分配的,采用延迟调度。

对于宽依赖,目前是物化中间结果在节点上,保持住父分区来简化错误恢复,和MR的map outputs相似

任务失败则重跑,如果丢失了如shuffle这样的中间结果会重新计算,目前不容忍调度失败

lookup操作提供随机访问元素,根据key在哈希分区上找

解释器整合

在scala解释器的基础上,做了改变:

class shipping:让工作节点通过http获得class的bytecode

修改了的code generation逻辑:为了让work节点能拿到引用,直接指向每行的实例(可能翻译不准确,理解不是很到位)

内存管理

Spark提供三种持久化RDD的存储选择:以反序列化java对象的方式存内存,以序列化数据的方式存内存,存磁盘

第一种最快,jvm可以本地化取RDD元素,第二种是高效内存化的方式

内存策略是LRU,新的RDD不够空间的时候,贯彻最近最少原则。同时,给予用户对RDD“持久性优先级”的控制

目前RDD有自己单独的内存空间,将来打算调研用一个统一的内存管理者在不同的实例之间分享式RDD

支持检查(checkpointing, 翻译为检查可能不太妥当,见谅)

尽管从RDD错误恢复的时候会使用血缘,但如果血缘链很长,就会很耗时,所以有必要对一些RDD进行 稳定存储时的检查

所以是为了长血缘链提供的,比如PageRank,短的不值得。

API是persist里的一个REPLICATE flag,主导权在用户手里,也在调研自动化检查。因为调度器知道每个数据集大小,第一次计算出来的耗时,所以应该可以做到优化最少恢复时间的选择。

同时,RDD的只读特性有利于检查,不需要中止和影响别的程序

转自:/article/3633407.html

转自

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐