论文笔记03:H-Scheduler Storage-Aware Task Scheduling for Heterogeneous-Storage Spark Clusters

发表于2018

一、存在的问题

Spark集群在存储介质(HDDs 和 SSDs)异构的问题,导致不同结点的读写速度不一致,没有充分地利用异构存储的优势。当前针对于异构环境下的调度,只考虑了计算资源,而忽视了存储设备异构性的问题。

二、提出的方法

2.1 创新点

考虑Spark集群上存储介质的不同对任务完成时间的影响,提出了对于异构存储介质集群存储感知的任务调度策略。

2.2 结果

实验结果显示,H-Scheduler能够降低Job 73.6%的执行时间。

三、背景&动机

  • 支持异构存储介质的HDFS;
  • 支持异构存储介质的HDFS和当前调度策略的匹配:
  • 由于网速的提升,对于数据本地化的影响对于任务执行的影响在降低;

四、设计

H-Scheduler在调度决策同时考虑将数据本地化存储介质类型这两个因素。

第一步 任务分类

H-Scheduler在调度之前应该知道处理每个任务的存储介质类型,根据任务的本地性和数据块存储介质的类型将任务进行分类,分为以下四种类型:

  1. SSD task
  2. remote SSD task
  3. local HDD task
  4. remote HDD task

四种类型的任务执行时间依次递增

将任务分成SSD中的任务和HDD中的任务两大类

第二步 任务优先级设置

根据四种任务类型来决定任务的执行顺序
任务的优先级为:

local SSD task > local HDD task > remote HDD task > remote SSD task.

第三步 节点的选择

在选择执行远程任务的时候,有以下三种策略:

  1. 随机选择
  2. 最多优先选择:选择拥有本地任务最多的节点
  3. 最少优先选择:选择拥有本地任务最少的节点

五、算法

算法1



适合的两个场景:

  1. 工作量是计算密集型的,任务数量很大;
  2. 能耗低要求较高,对于SLA截止时间要求较低。

六、实验

四种典型的工作负载:

  • Wordcount(500GB):CPU密集型任务
  • Sort(400GB):Shuffle密集型任务
  • Grep(600GB):I/O密集型任务
  • TPC-H:I/O密集型任务

三种调度策略进行对比:

  • Original:只考虑了数据本地化而没有考虑存储介质的差异;
  • Storage-type:只考虑了存储介质的差异而没有考虑数据本地化;
  • H-Scheduler:既考虑了数据本地化又考虑了存储介质的差异。

分别评估了两种job:

  • Single job on cluster:
  • Multiple jobs on cluster:同时运行三个job

影响H-Scheduler的三个重要参数:

  • SSD 任务的比例:比例越高,性能越好
  • SSD的副本数目:
  • SSD task,remote SSD task和HDD task之间时间的比例

七、相关研究

任务调度在大数据处理平台上处理策略:

提升数据本地化程度

通过提升数据本地化的程度来提升大数据平台的处理性能

处理方法 文章
通过延迟调度来获得更高的数据本地化程度 [11]
从随机网络的角度考虑map任务本地化的问题,并提出一种新的排队算法,以同时最大化吞吐量并最小化重负载条件下的延迟。 [24]
通过复制文件的方式 [12],[16]

处理异构计算资源

处理方法 文章
首次考虑异构问题并采取一种静态方法来计算任务的进度 [17]
探讨落后者/异常值的原因,并在其生命周期的早期发展原因和资源感知技术可以更智能地处理异常值。 [19]
利用历史信息来调整Map和Reduce阶段的权重,以便更准确地估计任务执行时间 [18]
使用K-means算法对历史数据进行分类,来提升对于任务阶段权重评估的准确性 [25]
使用动态负载重新平衡(更快的节点获取更多数据)来调度任务 [20],[21]

论文笔记02:Comparative Analysis of Energy-Efficient Scheduling Algorithms for Big Data Applications

发表于2018.7

一、解决的问题

Spark集群在反网络犯罪中的能耗问题

二、提出的方法

2.1 创新点

为了避免服务级别协议违反执行时间,提出了一种最优的任务调度算法,其具有通过权衡执行时间和能量消耗的最后期限约束,目的是去最小化大数据处理中的能耗问题。该最优算法能够找到接近最优的任务调度,以在小的shuffle分区中权衡消耗的能量和响应时间的优势。

2.2 结果

对比Spark内嵌的FIFO和FAIR算法,能耗上都有优化。

三、动机

  • 对于数据中心,不需要一味降低任务的执行时间。而在是截止完成时间之前,只需要提供按需的服务,更多的关心是能耗上的优化。

四、相关工作

考虑的主要因素 文章 方法
云计算当中优化物理机上的能耗问题
[14] 提出了考虑CPU,磁盘,内存,网络对于刀片服务器的能耗模型
[16] 提出了考虑CPU,磁盘,内存,网络,风扇对所有服务器的能耗的直接测量
Spark调度中主要考虑的是最小化工作执行的完工时间
[6] 他们模拟了执行器的应用程序成本和完成时间,因此提供了细粒度的资源分配方案
[19] 通过收集在同一个应用下的不同stage执行时间来预测应用的执行时间
[20] 提出了基于输入数据量,底层集群节点的大小和迭代次数的作业完成时间模型。
[18] 为了预测具有未知群集配置的大数据应用程序的执行时间,提出基于应用简档数据构建多个多项式回归模型
[21] 为Spark在线分析提供QoS保证,提出了基于熵在线并行分析调度

应对上述三个挑战的三种应对策略:

  • Spark的executors为了及时满足它们资源预留应将被放置到合适的节点上,避免太长时间的等待和低资源利用率;
  • 分配用于减少任务的资源应根据其随时间变化的需求进行重新平衡,以避免在将任何Spark应用程序提交到群集时浪费资源;
  • 在考虑Spark和MapReduce应用程序的位置感知的时候,为了减少局部性感知的竞争,Spark和MapReduce应用程序的任务分配策略都应该优化。

五、算法

算法1



适合的两个场景:

  1. 工作量是计算密集型的,任务数量很大;
  2. 能耗低要求较高,对于SLA截止时间要求较低。

算法2

算法B权衡能耗和执行时间,特别对于小shuffle的分区的情况下。

在小分区的情况下,算法B将任务分配给集群中最佳的一半执行程序,并尝试平衡在执行程序的另一半中花费的执行时间。

算法3

算法3将所有任务分成两个集合:

  • Set0:包含所有需要被探测的任务和当前p和e为0的任务;
  • Distribute:包含那些不需要被探测的任务。

RunTimeEachExe:记录着最优部分的执行时间

六、实验

基于HiBench的基准实验,在三种工作负载上实验:

  • Sort
  • TeraSort
  • PageRank

七、结果分析

算法B在保证有效降低能耗的前提下,优化了算法A的执行时间。因为基于贪心策略的算法A尽可能地将任务分配给评估标准的最佳过程,导致单个节点的过载并因此导致总体执行时间的增加。

算法B使得Shuffle分区的数量从10到40变化,确保可以使用集群中一半的执行程序。因此,算法B减少了单个节点上的负载,并且还加快了作业完成时间。

算法B的两点改进:

  • 任务分配策略
  • 不考虑数据局部性,以确保任务可以均匀地分布到最佳的一半过程

总而言之,算法B更加适合的场景:

  • 工作负载更少的任务
  • SLA的截止时间要求较高,节能要求较为宽松

Java中常见的几个OOM的情况

除了PC(程序计数器)以外,Java虚拟机内存区域的都有可能发生OOM(OutOfMemoryError)。

Java堆溢出

Java堆是用于存储对象实例的,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径避免垃圾回收机制被清楚,那么在对象数量到达最大堆的容量限制之后便会产生OOM异常。

解决思路

一般的手段是:先通过内存映像工具对Dump出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,也就是要先分清楚到底是出现了内存泄漏还是内存溢出。

  • 如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链。这样就能够找到泄漏的对象是通过怎么样的路径与GC Roots相关联的导致垃圾回收机制无法将其回收。掌握了泄漏对象的类信息和GC Roots引用链的信息,就可以比较准确地定位泄漏代码的位置。
  • 如果不存在泄漏,换句话说,就是内存中的对象确实必须存活着,那么此时就需要通过虚拟机的堆参数( -Xmx和-Xms)来适当调大参数;从代码上检查是否存在某些对象存活时间过长、持有时间过长的情况,尝试减少运行时内存的消耗。

-Xmx : 最大堆空间;

-Xms : 初始堆空间大小,如果初始堆空间耗尽,JVM会对堆空间扩容,其扩展上限为最大堆空间。通常-Xms与-Xmx设置为同样大小,避免扩容造成性能损耗。

虚拟机栈和本地方法栈溢出

由于在HotSpot虚拟机上并不区分虚拟机栈本地方法栈,因此栈容量只能由-Xss参数设定。在Java虚拟机规范中描述了两种异常:

  • StackOverflowError :如果线程请求的栈深度超过了虚拟机所允许的最大深度,就会抛出该异常;
  • OutOfMemoryError:如果虚拟机在拓展栈的时候,无法申请到足够的空间,就会抛出该异常。

对于这两种异常来说,存在着一些互相重叠的地方:当栈空间无法继续分配的时候,到底是内存太小还是已使用的栈空间太大,其实只是对同一件事情的两种描述罢了。

当在单线程环境下,无论是由于栈帧太大还是虚拟机栈容量太小,当内存无法继续分配的时候,虚拟机抛出的都是StackOverflowError 异常。

在多线程环境下,如果为每个线程的栈分配的内存越大,反而越容易产生OOM异常。每个线程分配到的栈容量越大,可以建立的线程数量就自然减少了,那么在新建立线程的时候就很容易把内存耗尽,产生OOM异常。虚拟机默认参数栈深度大多数情况下能够达到1000~2000,对于正常的方法调用(包括递归)是完全够用的。但是,在多线程环境下的OOM,就只能通过减少最大堆和减少栈容量来换取更多的线程数量。

方法区和运行时常量池溢出

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。当前的一些主流框架,如Spring、Hibernate,对于类进行增强的时候都会使用到CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成Class可以加载入内存,这样的情况下可能会造成方法区的OOM异常。

在经常动态生成大量Class的应用中,需要特别注意类的回收状况。这类场景还在:大量JSP或动态生成JSP文件的应用(JSP第一次运行时需要编译为Java类)、基于OSGi的应用(即使是同一个类文件,被不同的加载器加载也会视作不同的类)等。

直接内存溢出

直接内存(DirectMemory)容量可以通过-XX : MaxDirectMemorySize指定,如果不指定,则默认与Java最大堆(-Xmx指定)一样。

由于直接内存(DirectMemory)导致的内存溢出,一个明显的特征是在Heap Dump文件中不会看见明显的异常,如果发现OOM之后Dump文件很小,而程序中又直接或间接使用了NIO,就可以考虑检查一下是否为直接内存(DirectMemory)溢出异常。

参考资料