Resilient Distributed Datasets: A Fault-Tolerant Abstraction for In-Memory Cluster Computing 阅读笔记

背景

RDDs(Resilient Distributed Datasets):弹性分布式数据集,一种分布式的内存抽象,允许程序员在大型集群中进行计算的容错方式。

发展背景:因为传统的并行计算(MapReduce)采用的是非循环式的数据流模型,使得在迭代计算和交互式计算要进行大量的 I/O 操作,而 RDDs 正是解决这一点的抽象方法。

分布式集群概念图:

MapReduce 流程图:以 WordCount 为例

主要有两个操作:

  • Map:将数据进行一系列操作转换得到结果的过程,在从节点执行
  • Reduce:将数据整合成最终结果,在主节点执行

流程:input -> splitting -> mapping -> shuffling -> reducing -> result

产生的原因:

  • MapReduceDrayd 虽然已经被广泛应用到大规模的数据分析,但是缺乏对分布式内存的运用,使得需要重用中间结果的计算十分低效

低效的计算类型:

  • 如迭代式机器学习和图算法:PageRank、K-means聚类和逻辑回归。
  • Ad-hoc 查询:用户根据自己的需求,灵活的选择查询条件,系统能够根据用户的选择生成相应的统计报表

对传统 MapReduce 的改进:

  • 迭代式图计算系统 Pregel
  • Hadoop 提供了一种迭代式 MapReduce 接口

仍然存在的问题:

  • 仅支持特定的计算模式(循环一系列的 MapReduce 操作),没有提供更加通用的数据重用的抽象能够让用户直接在内存中装载数据

已有解决方法的缺点:

  • 基于集群的内存存储抽象:分布式共享内存,键值对存储,数据库等提供了基于细粒度更新可变状态的接口,运用这些接口时,达到数据容错的唯一方式是在不同的机器之间进行数据的冗余或者记录更新日志
  • 细粒度:针对数据集中的某个元素进行转换操作
  • 粗粒度:针对整个数据集进行转换操作,而不是针对数据集中的某个元素进行转换操作
  • 对于数据密集型任务来说代价高昂,因为他们需要在集群网络间复制大量数据,同时带来大量的存储开销
  • 数据密集型:大量的,复杂的、丰富多样的,快速变化的数据

本文的解决方法:

  • 提出了一种能够广泛运用于各种应用中高效的数据重用抽象,弹性分布式数据集(RDDs)。RDDs 是一个容错的、并行的数据结构,能够让用户明确地在内存中持久化中间结果,控制其分区以优化数据的放置和使用丰富的操作符对其进行处理

Rdds 的特点:

  • 读操作:粗粒度或细粒度
  • 写操作:粗粒度
  • 记录产生数据集的一系列转换操作(称为 lineage),而不记录真实的数据
  • lineage 过长时,会设置检查点(checkpoint
  • 如果 Rdd 丢失了,有足够的信息知道自己是如何从其他 Rdd 产生的,从而重新计算该分区,因此丢失的数据可以很快的回复,而不需要昂贵的代价

弹性分布式数据集(RDDs)

  • 弹性:横向有多个分区,纵向当计算过程中内存不足时可以写到外存上,当需要使用时交换到内存中
  • 分布式:分布在多台机器进行并行计算
  • 数据集:只读的抽象的数据集合

可以看做 lineage 图追踪不同版本化数据

简略 rdd 理解图

  • RDD 有多个分区,每个分区指向数据或其他的 RDD
  • 计算节点存储 RDD 的一个或多个分区
  • 分区就是依照特定规则,将具有相同属性的数据记录放在一起
如何产生 RDD
  • 从稳定的物理存储中的数据集创建
  • 由其他 RDDs 通过转换操作产生
用户可以对 RDD 的控制
  • 持久化:选择需要重用的 RDD 的存储策略,设定特定的 RDD 只存在硬盘上,或是设置优先级,当内存不够时,将内存中的哪个数据放到硬盘中
  • 分区: 可以选择 RDDs 的分区方式,可以通过用户自定义的规则进行分区
Spark 提供的编程接口
  • 转换(transformations):mapfilter
    • 所有的转换操作都是懒惰的
    • 转换操作会产生新的 RDDs,每个 RDDs 又包含多个分区,一系列的转换操作就构成了相互依赖的多个 RDDs 组成的有向无环图(DAG
  • 动作(actions):countcollectsave
    • 动作立即执行,返回结果
lineage 图示
  • 假设某一个分区数据丢失,则在相应的分区上重新进行转换重建丢失分区

RDDs 表示

  • 基于图表示 RDD 之间的关系(lineage
  • 一个公共接口表示每个 RDD 的信息
    • partitions():返回一组分区,是数据集的原子块
    • preferredLocations(p):返回分区 p 存放的位置
    • dependencies():返回该 RDD 的依赖(父亲)
    • iterator(p,parentlters):基于父亲计算数据集
    • partitioner():分区器,定义分区规则

依赖关系:

  • 窄依赖(narrow dependencies):父 RDD 的每个分区最多由子 RDD 的一个分区使用
  • 宽依赖(wide dependencies):一个子 RDD 依赖多个父 RDD

这样区分的原因:

  • 窄依赖关系可以在一个集群节点上顺序执行,节点故障时只需要重新计算父 RDD 的分区
  • 宽依赖关系需要所有父分区的数据计算完成后,再进行洗牌(shuffle),因此单个节点故障可能导致所有的子孙丢失数据,从而需要完全重新执行计算

实现

使用了 14000 的 scala 代码实现了 spark

  • 系统运行在 Mesos 集群管理器上
  • 可以与 hadoopMPI 和其他应用程序共享资源
  • 每个 spark 程序作为单独的 Mesos 应用程序运行,具有自己的驱动程序 master 和工作程序 workers,应用程序之间的资源共享由 Mesos 管理
  • 可以不用修改任何代码从 hadoophdfs 或 hbase) 读取数据
作业调度
调度思想
  • 调度程序会基于数据的位置使用使用延迟调度将任务分配给机器,实现任务所需的数据就在其运行的节点上

延迟调度:将任务发布到其所需数据的机器上,若机器已经被占用则进行延迟等待,直到可以运行该任务或者超过了等待时间则将任务运行到其他机器上

阶段(stage)
  • 当用户执行 action 操作时,调度程序会检查该 RDDlineage 图构建要执行阶段的 DAG
  • 每个阶段尽可能包含多的窄依赖关系转换,阶段的边界是宽依赖所需的洗牌操作或已经计算过得分区。
  • 对于宽依赖关系,会在父 RDD 上记录中间结果以简化故障恢复
  • 如果一个任务失败,一个阶段的父项仍然可用,则会在另一个节点重新运行;若某些阶段产生了数据丢失,则要重新提交任务计算丢失的分区
解释器整合
scala 解释器
  • scala 提供了 RubyPython 的交互式 shell
  • Scala 解释器为用户键入的每一行编译一个类,该类包含一个单例对象,该对象包含该行上的变量或函数,并在初始化方法中运行代码

例子:

Line1: var x = 5
Line2: println(x)
  • 解释器会编译一个包含 xLine1
  • 第二行编译为 println(line1.getInstance().x)
spark 解释器

做了两点改动:

  • 类传输:支持基于 HTTP 传输类字节码文件,这样 worker 节点就能获取输入的每行代码对应的字节码
  • worker 直接引用各个行对象的实例
内存管理
  • 序列化:将变量从内存中变成可存储或传输的过程
  • 反序列化:把变量内容从序列化的对象重新读到内存里

Spark 提供了三种持久化的 RDD 选项:

  • 反序列化 Java 对象的内存存储
  • 序列化数据的内存存储
  • 硬盘存储

内存替换:

  • 当新的 RDD 分区没有足够的内存存储时触发
  • 使用 LRULeast Recent Used 最近最少使用)策略替换
  • 用户可以设定 RDD 持久化优先级提供进一步控制
检查点的支持
  • 尽管在故障的时候可以通过 lineage 来恢复 RDD。但是对于具有非常长的 lineageRDD 来说,则恢复是十分耗时的,因此将一些 RDD 检查点存放到稳定存储中会减少恢复的耗时
  • 检查点技术主要对具有宽依赖关系的 RDD 比较有用,对于窄依赖关系的 RDD 用处不大

评估

迭代式机器学习应用

实现了两个迭代机器学习应用程序,逻辑回归和 k-means,以比较以下系统的性能:

  • Hadoop0.20.2 稳定的发行版本
  • HadoopBinMem:在首轮迭代中执行预处理,通过将输入数据转换成为开销较低的二进制格式来减少后续迭代过程中文本解析的开销,在 HDFS 中加载到内存。
  • Spark:基于 RDDs 的实现。
  • 由于 HadoopBinMem 首先要执行数据转换为二进制的任务,所以在首次迭代中速度最慢
  • 而在后续的迭代中,由于 HadoopBinMem 解析文本的开销较小,因此速度优于 Hadoop
  • Spark 均是速度最快的
  • 对于逻辑回归,Spark100 台机器上分别比 HadoopHadoopBinMem25.3 倍和 20.7
  • 对于计算密集型的 k-means 应用程序来说,Spark 仍然实现了 1.9 倍到 3.2 倍的加速
理解速度提升

会发现 Spark 甚至比内存存储二进制数据的 HadoopHadoopBinMem)还要快 20 倍。在 HadoopBinMem 中,我们使用了 Hadoop 的标准二进制格式(SequenceFile)和 256MB 大小的超大块,并且我们强制 HDFS 的数据目录位于内存文件系统中

由于以下因素,Hadoop 仍然运行缓慢:

  • Hadoop 软件堆栈的最小开销,运行一个 Hadoop 空作业,仅仅执行作业的初始化、启动任务、清理工作就至少耗时 25
  • 提供数据时 HDFS 的开销,每一个 HDFS 数据块,HDFS 进行了多次复制以及计算校验和操作(一致性问题)
  • 将二进制记录转换为内存中的 Java 对象的反序列化成本
PageRank

网页排名,是 Google 公司所使用的对其搜索引擎搜索结果中的网页进行排名的一种算法

  • Spark30 个节点上的速度比 Hadoop 提高了 2.4 倍,控制 RDD 的分区以使其在迭代中保持一致,将提高到 7.4
  • 加速也能几乎线性地扩展到 60 个节点上
数据集在内存中不同百分比的表现
  • 随着数据集在内存中的比重增加,迭代时间随之减小
交互式数据挖掘

为了演示 Spark 交互式查询大数据集的能力,我们用它来分析 1TB 的维基百科页面浏览日志(2年的数据),在输入的数据集上获取所查询页面的浏览总数:

  • 全部页面
  • 页面的标题能精确匹配给定的关键词
  • 页面的标题能部分匹配给定的关键词
  • 实验分别在整个, 1 2 \frac{1}{2} 21 1 10 \frac{1}{10} 101 的数据集上查询的响应时间,结果表示比直接在磁盘上操作快了几个数量级
  • 实验结果表明 RDD 缓存会使得 Spark 成为一个交互式数据挖掘的强度工具

总结

  • RDDs 是一种高效、通用、容错的用于集群应用程序中共享数据的抽象,不记录真实数据,而是记录数据转换的关系和数据存放的位置
  • RDDs 提供粗粒度转换的操作,使得能够用 lineage 有效的恢复数据
  • 对于窄依赖关系直接计算父 RDD 的相对应的分区,宽依赖关系通过在父 RDD 记录中间结果简化故障恢复,对于长的 lineage 则设置检查点简化恢复操作
  • RDDs 支持用户对数据进行持久化和分区操作
  • 调度主要思想是移动计算而不是移动数据
  • 内存不足时可以暂时存放到外存中,有需要再换进内存
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈乐乐happy

觉得对你有用的话可以打赏打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值