2. 背景
Hadoop实现了类似Google的MapReduce。Hadoop运行在一个称为HDFS人分布式文件系统上,类似GFS每个数据块存储三份副本。用户提交的作业由一个map函数和一个reduce函数组成。Hadoop拆解每个job为多个task。首先,map任务处理每个输入的数据块(物理大小为64MB)并以键值对方式生成中间结果。每个输入数据块对应一个map任务。接下来,reduce 任务传递每个KEY的中间值列表到用户定义的reduce 函数,最终生成作业的输出结果。
由一个master来执行Hadoop中的作业调度,Master管理多个slave。每个slave有固定的map slot数量和reduce slot数量,用来运行task。物理上,管理员为一个或者两个CPU核设置一定数量的slot。Master依据Slave每隔几秒以心跳的方式报告空闲的map s和reduce的slot数量分配Task。
Hadoop的默认调度器以FIFO顺序运行作业,并有五个优先级。当调度器收到一个表明map slot 或者reduce slot释放的心跳,调度器以优先级顺序和提交时间扫描所有作业并查找一个合适的task。针对map侧任务,Hadoop使用一个类似Google的MapReduce论文提到的数据本地化优化方案:选择一个job后,调度器从job中挑选一个任务数据靠节点最近的map任务(优先是相同的节点,其次是在相同的机架,或者最终在一个远程的机架)。
3. 延迟调度
回想下,我们的目的是统计多集群对公平影响最小(给定新作业公平的快速分配资源)和达到较高的数据本地化。在这章节,我们分析一个简单公平分配算法回答两个问题:
1. 新作业应如何重分配资源
2. 数据本地化如何达到预期目标
回答第一个问题, 我们考虑两个方法去重分配资源:杀死正在执行的任务,另外就是等待任务结束后分配slots给新的作业。杀死任务的优势是瞬间释放资源,但这个会导致被杀死的任务执行过的工作量所消耗资源被浪费。当集群是多用户间共享且作业响应时间时间大于任务执行平均完成时间,我们建议等待更好些。这些条件适用于Facebook工作负载和Yahoo!,因此我们基于HFS等待。
选择等待,我们转移关注点到数据本地化。我们发现当严格公平分配时会出现以两个本地化问题---head-of-line scheduling (队头调度)和sticky slots(热点slots)。在这两者情况下,为维护公平性一个调度器会强制在一个没有job本地数据的节点上启动任务。我们提出一个叫延迟调度的算法,可临时放宽公平性以提高job任务的数据本地化,等待一个节点有任务的数据的调度时机。我们分析等待数量(算法一采用的次数,算法二采用的时间)对数据本地化和job完成时间的影响。
为简单起见,我们最初聚焦在数据本地化级别上:任务放置位置与它的输入数据在相同的节点上。在章节3.6中我们考虑机架位置对数据局部化的影响。
3.1 绝对公平分配算法
在作业之间一个简单的公平分配方式是总是把释放的slot分配给任务运行最少的作业。只要slots释放的足够快,分配结果将会满足最大最小公平性。为达到预期数据本地化,我们从队头(head-of-line)job中尽可能地寻找一个数据本地化任务,就像Hadoop的FIFO调度器。这个算法伪代码如下:
算法1 绝对公平分配
//when a heartbeat is received from node n:
if n has a free slot then
//sort jobs in increasing order of number of running tasks
for j in jobs do
if j has unlaunched task t with data on n then
launch t on n
else if j has unlaunched task t then
launch t on n
end if
end for
end if
在我们的HFS第一个版本里面我们实现了这个算法。我们分别实施这个算法到map slots和reduce slots分配。另外, 我们仅对map侧任务使用数据本地化检验,因为通常reduce 任务需要从所有节点读取大致等量的数据。
3.2 调度响应能力
当一个新的job提交到集群,我们的考虑的第一个问题是如何去重分配这个作业。最理想情况是,我们希望一个作业的公平配额是
个slots,好比它独立运行在一个小的有
个slots的私有集群中。假设,作业
耗费了
秒后运行在私有集群上。如果作业
被提交到一个采用等待的共享集群中,我们预测作业
耗时多久会收到它的slots配额。集群中如果所有的slots已经被分配,作业
的slots分配比率将是任务完成比率。假设任务平均时长为
,集群拥有
个slots。那么一个slot被释放的平均时间为
秒,因此作业
预期要等待
秒后才能得到所需的所有slots。这个等待时间相比于运行时间可忽略不计。
如果以下条件之一成立,那么等待将不会显著的影响作业的响应时间:
1. 大量的作业:当有大量作业运行,但每个作业占用集群很少一部分配额,值很小。
2. 小作业:作业包含少量的任务(我们称作它们为”small jobs“),也有一个小的值。
3. 长作业:作业的调度耗时略大于任务平均耗时
追溯Facebook的workload,我们发现大多数任务执行时间短而且大多数作业都是小作业 ,甚至集群处于负载情况下slots也能够被快速重分配。图1呈现了2009年10月超过一周的长的map任务时长、作业时长和reduce任务时长的CDF分布图。map任务时长的中位数是19秒,明显小于作业时长的中位数84秒。Reduce耗时更长(中位数是231秒),发现这样的情况是因为大多数作业没有大量的reduce任务,因此少量作业耗时长的reduce 贡献大部分的CDF
在大多数特定类型的slots已经被分派期间我们也预测了slots的释放速率。Map侧slot有21%的时间超过95%的slot被占用,平均看,一个负载周期期间,每秒释放27.1个slot(总共3100个slot)。Reduce侧slot有4%的时间超过95%的slot被占用,总共3100个slot,每秒释放3个slot。依据这些链路数据,释放比率足够让83%的job在10秒内启动,因为83%的job的map任务少于271个组reduce任务少于30个。
最终,我们在Facebook使用雅虎一个3000个节点的集群观察了跟上面描述一样的任务和job的执行时间,使用数据分析和 ad-hoc查询(一种数仓的即席查询,用户根据需求灵活选择查询条件):job调度时间的中位数是78秒,map任务的调度时间中位数是26秒,reduce任务的调度时间中位数是76秒。
3.3 绝对公平分配的数据本地化问题
使MapReduce调度复杂的主要方面是需要把任务放置在靠近输入数据。数据本地化会增加吞吐量,因为在集群中的网络带宽低于集群的磁盘带宽。任务运行在包含数据的节点(node locality)是最高效的,当这点做不到的时候,任务和数据属于在同一个机架(rack locality)内也快于off-rack。现在,我们只考虑数据本地化节点。我们描述绝对公平分配出现的两个本地化问题:队头调度(head-of-line)和热点slots(sticky slots)
3.3.1 队头调度(Head-of-line Scheduling)
第一个本地化问题发生在小作业上(小job定义:小量输入文件和小量数据块需要读取)。这问题是,在算法一(只有少量任务运行)中无论何时一个作业到达有序列表的头部,它的任务总是启动在下一个即将释放的slot上,无论这个slot在哪个物理节点上。如果这个队头作业比较小,数据不太可能在给定的节点上。例如,一个job的数据在10%的节点上,那么数据本地化将仅达到10%。
我们在Facebook的一个没有使用的延迟调度的HFS版本上观察队头调度问题。图2呈现了2009年3月运行在Facebook上不同大小的作业(不同数量的map任务)的数据本地化。(每个输入数据块有一个map任务相对应)每个点代表一个量化的作业大小,第一个点表示1-25个map任务的作业,节点本地化仅达到5%,机架本地化达到59%。不幸地是,在Facebook大多数job都是小job。实际上,Facebook 58%的job都落入了第一个bin(1-25个map任务)中,小作业如此常见是因为ad-hoc查询和周性报表作业工作在上小数据集上。
3.3.2 sticky slots
第二个本地化问题,sticky slots,会发生在使用公平分配的大作业。一个作业被重复分配到相同的slot的倾向称为sticky slots(笔者理解为slot倾斜或者热点slot)。例如,假设有10个作业,每个作业有10个任务正在运行,集群有100个节点,每个节点有一个slot。假设作业在节点n上完成了一个任务,节点n现在请求一个新的任务。就在此时,作业
有9个任务正在,其它作业10个任务正在运行。因此算法一分配节点n上的slot再次给作业
。所以,在稳定的状态下,作业绝不会偏离他们原来的slots(jobs never leave their original slots.)。这会导致数据本地化变差,因为输入文件跨越了整个集群,因此每个job需要在每台机器上运行一些任务。
sticky slots的影响依赖于作业的数量,每个节点上slots的数量(我们记为L),文件系统每个数据块的副本数量(我们记为R)。假设作业占集群的配额比例是
。那么任何给定的数据块
,不在作业
的slots对应的节点上的概率是
:这里的
是
的副本数,每个副本所在节点有
个slots,一个slot不属于作业
的概率是
。因此,作业
可达到的最大的数据本地化是
。 在图3我们绘制了不同的
和
及不同并发作业的数量对数据本地化的限制(基于等额分配的集群)。即使
和
很大,15个作业并发时数据本地化低于80%同时30个作业并发低于50%
有趣的是,由于Hadoop中如何计算正在运行任务的一个bug,sticky slots不会发生在Hadoop。Hadoop任务请求允许重命名最终输出的文件名后进入一个叫"commit pending"状态后结束他们的处理。Master中的作业对象认为一个任务正处于运行状态,然而Slave中认为这任务处于"commit pedning"。因此另外的作业可以分配到这个任务的slot。这是一个bug(破坏了公平),它减少了吞吐量和响应时间的影响。尽管 如此,我们解释这个sticky slots问题用来提醒其它系统设计者。例如,sticky slots发生在Dryad 的报告。在章节5,在一个没有sticky slots bug的Hadoop的版本中吞吐量低2倍。
3.4 延迟调度
我们提出的这些问题发生在调度沿着一个严格有序的作业队列强制去调度一个没有本地数据的作业。我们提出一个被称作为延迟调度的简单的技术来解决这些问题。当一个节点请求一个任务时,如果队头的作业不能启动一个数据本地化任务,跳过这个作业并查找随后的作业看是否有数据本地化任务。不论怎么样,如果一个作业跳过的时间足够长后,我们允许作业启动为非本地化任务,以避免任务没有调度机会。延迟调度背后的关键含义是第一个被释放的的slot节点不太可能拥有队头作业的任务需要的数据,在接下来的几秒内一些任务会很快就结束,将释放一些slot。
在这章节,我们考虑一个简单版本的延迟调试,我们允许一个作业跳过D次。算法伪代码如下:
Algorithm 2 Fair Sharing with Simple Delay Scheduling
Initialize j.skipcount to 0 for all jobs j.
when a heartbeat is received from node n:
if n has a free slot then
sort jobs in increasing order of number of running tasks
for j in jobs do
if j has unlaunched task t with data on n then
launch t on n
set j.skipcount = 0
else if j has unlaunched task t then
if j.skipcount D then
launch t on n
else
set j.skipcount = j.skipcount +1
end if
end if
end for
end if
注意,一旦一个作业跳过D次后,我们会在不重置作业的skipcount情况下武断的启动这个作业的任务为非数据本地化任务。不管怎么样,只要作业再次启动了一个数据本地化任务后,我们重置skipcount为0。在分析延迟调度的小节中解释了它的原理。
3.5 分析延迟调度
在这个章节,我们探索在算法2中D的极限值对数据本地化和响应时间效果最好,我们发现:
1. 非本地化任务随D呈线性递减
2. 为达到一个给定级别的数据本地化需要等待的时间占任务平均时间的一少部分并且随每个节点的slots数量(记为)递减
我们假设我们有一个集群有个节点且每个节点有
个slots,这样slots总数为
。另外,作业j要处理的剩余数据所在节点集合记为
,我们称为作业j的"首选"节点,作业j的首选节点占比为
。为简化分析,我们假设所有任务时长相同为
且时长与集合
无关(例如,每个作业在每个节点上有一个很大的输入文件或者每个作业有一个不同的输入文件)。
我们首先量化D值对数据本地化的影响。假设作业j远远低于它的公平份额。那么作业j在每个释放的slot上有数据的概率为。如果作业j最多等待D个slots后可以启动为非本地化任务(non-local tasks),那么作业j不能启动本地化任务概率是
,这个概率随
呈指数递减。例如,一个作业的数据在10%的节点(记:
)上有数据,当
=10有65%机会启动一个本地化任务,当
=40有99%的机会。即:
第二个问题是低于公平份额的作业需要等待多久能启动一个本地化任务。因为集群中有个slots,平均每
秒释放一个slot。因此,一旦一个作业
达到队列头部,它将最多等待
秒被允许启动一个非本地化任务,假设它一直呆队列头部。如果
值很大,这个等待时间远远低于任务平均时长。特别是,等待一个本地化任务花费时间可能少于运行一个非本地化任务:在我们的实验中,本地化任务运行时间比非本地化任务快2倍以上。还请注意,在一个固定数量的节点,等待时间随每个节点slot的数量线性减少。
为达到一个设想级别的本地化,我们对如何设置D做了一个近似分析的推断。假设在一个有个节点的集群中,作业的任务数为
,每个节点有
个slots,副本因为
,我们期望本地化任务比例大于
。我们将计算一个作业在整个生命周期内还有N,N-1,,,1个任务时启动一个预期的本地化级别任务的平均概率。当作业
还剩
个任务需要启动,启动预期级别的本地化任务概率为
,因为一个给定的节点没有作业
的输入数据块的副本概率为
。因此在这点作业
启动一个本地级别的任务概率是
,给定一个跳过数量
,作业
预期本地化任务至少是
求解,我们需要设置:
例如:令=0.95,
=20,
=30,我们需要
。也就是,一个作业等待一个本地化任务最大时间是
。例如,设置每个节点的slot数量为
,等待时间为任务平均时长的2.8%
四. Hadoop 公平调度设计
在章节三描述了简单的公平调度器,集群资源充足,同时在少量用户情况下,每个作业可从集群中获得等额的slot。但是,要处理类似Facebook这样大的公司,章节三的方案存在一些问题:
1. 有些用户运行的Job数量远远大于其他用户。这时我 们可能希望做到用户级的公平调度,而不是基于Job的公平调度
2. 用户想控制他们的Job调度。比如,一个用户批量提交少部分Job,希望以FIFO的顺序执行并得到有序的结果。
3. 如果集群处于大量耗时长的Task负载下,需要对些Job作推测处理
在我们设计的Hadoop的公平调度器(Hadoop Fair Scheduler 简称HFS)中处理了这些问题。HFS使用两级调度。一级调度,HFS使用加权的公平分配策略跨资源池分配任务的Slot。默认每个用户有一个slot资源池,也可以为特别的Job(Particular Workloads)创建单独的资源池。二级调度,使用优先级FIFO或者二级公平分配(a second of fair sharing)的方式分配池中的Slots给池中的Job。图四展示了一个层级池示例。对HFS简单扩展后支持多级池,或者在一个池中使用FIFO、公平分配等更多的策略。
我们提供一个超出标准的加权公平分配特性以支持生产作业。每个池能给定一个最低配额,即使池的公平配额低于最小配额,可以确保它包含的作业 有一个最低配额的slot。HFS总是优先考虑满足最低配额而不是公平配额,并且可以Kill Task以满足最低配额。管理员期望基于一个Job需要满足某个SLO需要的Slot数量来设置最低份额。如果所有池的最小配额的总额超过集群中的Slot总数,HFS会有告警日志,并且按比例减少最低配额直到它们的和少于Slot总数
最终,尽管大多数时候HFS使用等侯去重新分配资源,它也支持Task Killing。通过Task Killing来防止有问题的Long Task的Job或者用户占用集群中大份额资源。HFS使用两个Task Killing 超时时间。第一,每个资源池有一个最小份额超时时间(a mininum share timeout)记为Tmin。一个Job提交给资源池后在Tmin内资源池没有最低份额资源提供,将会自动终止任务来满足资源池的最低份额。第二,提出了一个全局公平分配超时时间,记为Tfair,如果一个池的公平分配份额不能满足,经过Tfair时长后会终止任务。管理员基于Job的SLO为每个Production Pool设置Tmin,基于用户可容忍的延迟级别去设置一个较大的Tfair。当出现前述情况后,会选择终止任务。挑选终止的任务具有两个特征:
1. 使用Slots超过池的公平配额
2. 最近在池中启动的。
4.1 HFS分配Task
不论何进一个slot被释放,HFS通过两阶段处理一个任务分配给slot:首先,根据层级调度策略创建一个有序的作业列表。第二,我们扫描下这个列表找到一个作业启动它的一个任务,在一个有限时间内使用延迟调度跳过节点上没有数据的作业 。同一个算法可独立应用于Map slots和Reduce slots,然而reduce 我们不使用延迟调度,因为通常需要从所有节点上读取数据。
去创建一个有序Job列表,我们使用一个递归算法。首先,按池的最低配额排序,配额最低的池放在队头。另外,在每个资源池的内部,基于FIFO或者公平分配的策略对Job进行排序。我们实现的延迟调度与章节3.4稍有不同,考虑进去了一些实际上的问题。第一,一个Job为生成一个本地Task等待时间不是使用跳过D次来决定,而是设置了一个最大等待时间(a maximum wait time)。当集群中大量的Slot由于Long Task导致不被释放或者slot的释放速率太慢的时候可确保Job启动在一个可控的时间范围内。第二,当一个Job不能被启动为节点局部化Task的时候,我们使用二级延迟调度可以达到机架局部化Task。
二级延迟调度:
1. Job等待W1秒去寻找节点局部化Task
2. Job等待W2秒去寻找机架局部化Task
这个算法伪代码如下:
Maintain three variables for each job j, initialized as
j.level = 0, j.wait = 0, and j.skipped = false.
when a heartbeat is received from node n:
for each job j with j.skipped =true, increase j.wait by the time
since the last heartbeat and set j.skipped = false
//有空闲的资源
if n has a free slot then
//使用层级调度策略排序Job
sort jobs using hierarchical scheduling policy
for j in jobs do
//启动Local-Task
if j has a node-local task t on n then
set j.wait = 0 and j.level = 0
return t to n
//启动Rack-Task
else if j has a rack-local task t on n and ( j.level >= 1 or
j.wait >= W1) then
set j.wait = 0 and j.level = 1
return t to n
//任意启动一个Non-LOCAL Task Non-Rack Task
else if j.level = 2 or ( j.level = 1 and j.wait >= W2) or
( j.level = 0 and j.wait >= W1+W2) then
set j.wait = 0 and j.level = 2
return any unlaunched task t in j to n
else
set j.skipped = true
end if
end for
end if
每个Job初始化时"Locality Level "值为0,这时它可以启动本地化Task。如果Job等待了至少W1秒后,Job的"Locality Level"值变为1,这时Job可以启动Rack-Local 任务。如果Job等待至少W2秒后,Job的"Locality Level"值变为2,这时Job可以启动任意一个Non-Local Task 和Non-Rack Local Task。最终,如果一个Job曾经启动了一个与"Locality Level"值不匹配的Task,此Job的"Locality Level"会退回到上个值,跟章节3.5.1描述一样。这个算法可简单的去扩展,应用到多层级网络集群中启动更多层级的的本地化任务。
我们预期管理员去设置等待时间W1和W2,基于slot在集群中的释放速率和所需的位置级别。例如,在Facebook,数据副本R=3,机器M=620时,我们发现当集群处于加载状态时,每秒可以释放27个map slots 。因此,设置W1=10s,D=270。这足够让Job的k=1时,本地化达到=73%,k=10时本地化达到90%。
环境 | 节点数 | 硬件和配置 |
Amazon EC2 | 100 | 4 2GHz cores, 4 disks and 15 GB RAM per node. Appears to have 1 Gbps links. 4 map and 2 reduce slots per node. |
Private Cluster | 100 | 8 cores and 4 disks per node. 1 Gbps Ethernet. 4 racks. 6 map and 4 reduce slots per node. |
表1:用于评估的试验环境
五、评估
我们评估延迟调度和基于Facebook上的压力负载(workload)做一系列的基准测试,基于测试被设计用于层级调度测试、延迟调度压力测试、敏感性分析、测量一个调度器程序的开销。
我们在两个环境中跑试验:在亚马逊可扩展云计算平台(EC2)上有100个私有的 Node。在EC2上, 我们使用的VM基本上占据了整个物理机节点。两个环境都是非典型的大的MapReduce安装环境,因为两个环境拥有非常高的对分宽带(bisection bandwidth)。这个私有集群跨越4个机架,同时EC2不提供整个集群的拓扑的信息,而且经测试表明节点间通信带宽可达到1Gbps。因此,我们的试验低估了数据局部性带来的性能改善。 我们运行修改过的Hadoop0.20的版本,每个数据块配置128M,因为这可改善性能(Facebook在Production上使用这个配置)。我们基于硬件的容量在每个集群中设置每个节点的Slot数量。表1列出了在每个环境中的硬件和slot数量。
5.1 宏基准测试
以多用户工作负载方式去评估延迟调度和HFS,我们基于在Facebook的EC2上的压力负载(workload)运行一系列的宏基准测试。2009年10月在Facebook一个多星期内,我们从作业的分布规律上随机抽取100个Job间隔时间和输入数据大小生成了一份作业提交计划。基于Hive基准测试,我们使用三类不同压力负载运行这份Job提交计划:一份高IO负载,所有Job受IO限制;一份高CPU负载,所有Job都受CPU限制;最后一份混合型负载,所有Job包含在这个基准测试中。针对每个压力负载,我们比较响应时间和在FIFO调度、公平调度以及延迟调度下的数据本地化。在展示结果之前,我们现在详细描述下我们的试验方法。
最开始我们生成一份普通Job提交计划表由所有的试验分配。我们选择使用相同的作业计划运行在不同的试验中以便找到叫elements of "luck"(不知道怎么翻译),比如,一份大Job提交后提交一个小的Job在所有的试验中发生相同的次数。不管怎么样,这份计划足够长,包含各种各样的行为(这份计划包含100个Job)。为生成这计划表,我们首先从Facebook的trace(估计类似链路分析日志)随机抽取Job之间的间隔时间。随着平均时间14秒,所有的提交计划共耗时24分钟,作业间隔时间分布大致呈指数型。
Bin | #Maps | %Jobs at Facebook | #Maps in Benchmark | #Job in benchmark |
1 | 1 | 39% | 1 | 38 |
2 | 2 | 16% | 2 | 16 |
3 | 3-20 | 14% | 10 | 14 |
4 | 21-60 | 9% | 50 | 8 |
5 | 61-150 | 6% | 100 | 6 |
6 | 151-300 | 6% | 200 | 6 |
7 | 301-500 | 4% | 400 | 4 |
8 | 501-1500 | 4% | 800 | 4 |
9 | >1501 | 3% | 1800 | 4 |
表格2:Job大小的分布
基于Facebook压力负载我们也生成Job的数据输入大小,以查看每个Job的Map侧task的分布数量和创建适合的数据集(因为每个map 侧的task输入的数据块大小为128M)。我们将 Job大小量化到9个bins中,如表格2所示,以便能够在相同的bin中和跨试验比较Job。我们注意到大部分是小Job,但最后一个bin,大多数Job超过1501个map task,包含些非常大的Job:98%的job有3065个map tasks,99%是3846个map task,99.5%是6232个map task,另外在这周末我们观察到最大的 Job有超过25000个map task。我们选择4800 maps作为我们的
我们将提交计划用于三个压力负载(IO-heavy ,CPU-heavy,and mixed),通过改变Job特征结构去评估算的影响。我们从Hive基准测试中选择实际的Job运行在每种情况下,其中包含来自Pavlo 等人的MapReduce基准测试的Hive四个查询的版本:文本搜索,一个简单查询过滤,一个聚合和一个join。
最终,我们在三个调度程序下运行每个workload :FIFO(Hadoop's default scheduler),公平分配(算法1),和延迟5秒的公平分配调度。为简单起见,我们将每个作业作为单独的用户提交,这样做就可以平等地共享集群资源。
5.1.1 IO-heavy 压力负载结果
为评估我们的算法在高IO负载情况下的性能,我们从Hive基准测试中挑选文本搜索类Job,这些Job通过扫描数据集和输出包含特定格式的记录。仅0.01%的记录包含这些样式,因此这Job几乎完全受磁盘IO限制。
图5、6、7呈现了我们的结果。
图5:在各类bin范围内高IO负载情况下job的运行时间的CDF。公平分配极大改善了小job的性能,以减缓大Job作为代价。延迟调度进一步改善性能,特别是针对中等规模的Job。
图6:在IO高负载下每个bin的数据本地化。
图7:在高IO负载中,对Job调度时延迟调度的平均加速比高于绝对的公平分配调度。黑线表明了标准差。
首先,图5呈现了一个Job在不同大小的文件下运行时间的CDF。在FIFO调度下我们发现大约30%的小Job明显缓慢向下,因为它们必须等待一个大Job完成。可以切换为公平分配调度策略解决这个问题,无论什么时候启动Job,让所有的小Job执行时效几乎一样。调度性能上,公平分配调度策略比FIFO调度策略最大提高了5倍。另一方面,公平分配调度策略在调度大Job时调度效率慢下来了(在 bin 9中),因为大Job会让其它Job先运行。当两个Job同时启动的时候,大约最大可慢下来1.7倍。公平分配预期的结论:通过适度延缓对大Job的调度为代价来提升小Job的响应时间和改善调度时间可预测性的准备性。
第二,我们观察到在公平分配调度策略上增加延迟调度策略会全面改善性能。如图6所示,从所有的bin文件中看,延迟调度带来了99%-100%的数据本地化,然而小Job的数据本地化低于Hadoop的默认调度策略和完全公平分配策略。对响应时间的影响更为微妙,为清楚说明这点, 我们绘制了图7,展示了每个bin文件从公平分配向延迟调度改变后这些Job平均加速的过程。我们发现延迟调度的加速对极小的Jo影响是轻微的。一部分的因是多数时间都是由小Job组成,另一部分原因大部分时间集群的实际负载不足,另外小Job的启动时间大致一样,因此绝大多数据Job不会面对网络连接。针对大Job延迟调度的效果也不多。大Job在每个节点上有很多输入数据块,因此Hadoop的默认调度算法也可达到高的数据本地化。然而,中等规模的Job加速明显,在bin5(100 maps)中使用延迟调度运行Job平均快44%。
5.1.2 高CPU负载运行
去创建一个高CPU负载环境,我们修改Hive基准测试里的文本搜索job为对每条输入记录运行一个计算密集类用户定义的函数,但是仍然仅输出0.01%的记录。这迫使这些Job 2至7次变慢(大job这方面影响更明显,因为大多数时间Hadoop 的job都是小 job组成)。我们观察到数据本地化级别与高IO负载环境下非常接近,因此在这里我们没有去绘制这些了。然而,在图8我们绘制了Job响应时间的分布函数(CDF)。我们注意到两个行为:首先,跟以前一样,公平分配调度策略改善小job的响应时间,但它的效果是比较好的(有些Job速度提升差不多20倍),因为这个集群负载是比较重地(我们继续运行相同的数据,但job运行成本更更高)。第二,延迟调度效果不明显,因为这个工作负载环境限制了CPU,但这也不影响延迟调度的性能。
5.1.3 混合类工作负载
我们在Hive基准测试中运行四类Job来构建一个混合的工作负载环境。除了文本搜索Job使用在重IO工作负载中,这还包括:
A. 一个简单IO密集型查询(相当于一个PageRank)。
B. 一个通集密集型的聚合job(计算数据集中的每个IP的广告收入)。
C. 一个复杂job查询转换为连续的四个job()。
Bin | Job Type | Map Tasks | Reduce Task | #Jobs Run |
1 | select | 1 | NA | 38 |
2 | text search | 2 | NA | 16 |
3 | aggregation | 10 | 3 | 14 |
4 | select | 50 | NA | 8 |
5 | text search | 100 | NA | 6 |
6 | aggregation | 200 | 50 | 6 |
7 | select | 400 | NA | 4 |
8 | aggregation | 800 | 180 | 4 |
9 | join | 2400 | 360 | 2 |
10 | text search | 4800 | NA | 2 |
表3:在混合工作负载平台里每个bin的Job类型和大小
针对这个实验, 我们分割bin 9为两个小的bin,其中一个包含2个join job(两个join job会被转化为一个拥有2400个map的job,紧跟着是三个小job),跟以前一样另外一个包含两个4800-map的job。 在表3我们列入每个bin的代表job。不像我们前面两个工作负载测试只有map侧job,这个负载测试还包含Reduce任务,因此我们在表3也列出了每个job Reduce任务的数量。
图9我们绘制每个bin中的job的响应时间分布(CDF)。如上个试验, 公平分配调度策略对小job响应时间性能改善不明显,当有大job时明显变慢。因为聚合job消耗时间比仅有map任务的job长不少(reduce阶段有繁重的网络通信),在图10我们也绘制了每个bin分别实现的加速。黑柱状条呈现了公平分配下的FIFO调度策略下的加速,高亮柱状条呈现了延迟调度下的FIFO策略的加速。这些只有map阶段的小Job(bins 1和bins 2)在公平分配调度策略下加速明显。Bin 3没有达到一个相当高的加速,因为这些job耗时较长(它的中位值大约是100秒,在延迟调度下bins 1和bins 2的中位数是32秒)。然而,最大的bins除外,这些jobs都从公平分配和延迟两种调度策略中收益。我们也发现延迟调度作用于IO密集型map 侧jobs(1,2,4,5,7,10)的收益要大于reduce 侧任务(因为小job运行时间的一小部分耗费在记取输入数据)。
5.2.2 小任务的延迟调度
基于队头调度讨论延迟调度在小型job的工作负载下对数据本地化和吞吐量的影响,在私有集群中我们的运行工作负载包含3、10、或者100个map 任务的过滤型job组成。在每个工作负载下,我们基于job大小挑选 job数量,因此实验耗费10-20分钟。我们比较了公平分配和FIFO两种调度策略使用延迟调度和非延迟调度两种情况(W1=W2=15,延迟时间均调为15秒)。FIFO执行结果和Fair Sharing类似,因此我们仅展示两者中的一组数据。
图12呈现了正常工作负载下的行时间,整个表4呈现了每个调度器数据本地化达到情况。延迟调度增加吞吐量情况如下:3个map任务的job提升1.2倍,10个map任务job提升1.7倍,100个map任务的job提升1.3倍,提升数据本地化达到了75%,同一个机架下数据本地化达到94%。10个map任务job吞土增速明显高于100个map任务job原因是100个map任务的数据分散率更好。甚至没有使用延迟调度机架本地化效果好的原因是我们集群只有四个机架。
5.2.3 热点节点延迟调度
如章节3.3解释,在hadoop中由于一个bug不会发生调度热点现象(把sticky slots翻译为了热点),我们在一个hadoop版本修复这个bug去测试并量化热点调度的影响。我们在EC2上运行这个测试。我们生成一个大的180GB的数据集(每个节点2GB),提交5至50个并发扫描job,并去测量所有job完成时间和数据本化地化情况。图14和13呈现了这些结果以及没有使用延迟调度的结果(W1=10s)。没有延迟调度情冲下,更多的并发job数据本地会降低----5个job的数据本地化为92%,50个job的降低为27%。在所有例子中使用延迟调度策略后数据本地化提升为99-100%。吞吐量呈线性增长,10个job1.1倍的增速,20个job1.6倍的增速,50个job2倍的增速。
图13:Node locality in sticky slots stress test.As the number of concurrent jobs grows,locality falls because of sticky slots.
图14:Finish times in sticky slots stress test.When delay scheduling is not used,performace decrease as the number of jobs increase because data locality decreases.In contrast, finish times with delay scheduling grow linearly with the number of jobs.
5.3 敏感性分性
我们在EC2环境下通过一系统列的实验测量等待时间对延迟调度作用于数据本地化的影响(简述为:延迟时间对本地化的影响)。我们使用两个小job运行这个实验:4个map任务和12个map任务,去测量延迟调度对队头调度的缓和情况。 我们在每个实验中运行200个job,50个job会在任意时间被激活。我们对每个节点的数据本地化等待时间设置不一样,记为W1,W1值从0秒到10秒不等。这里不考虑机架本地化,因为在EC2环境中我们没有关于机架的信息。然而,机架本地化比节点本地化比例高,因为这儿每个机架有更多的slots.图15呈现了这个结果。我们观察了没有使用延迟调度的时,记W1=0,两个job分别为4个map任务和12个map任务,它们的数据本地化只有5%和11%。W1=1秒时数据本地化提升为68%和80%。增加延迟时到5秒时本地化接近完美(接近100%)。最终,延迟时间增加到10秒时,4个map任务的job我们得到100%本地化,12个map任务job得到98%的本地化。
5.4 调度器开销
在我们100个节点实验中,HFS没有新增任何明显的调度资源开销。去测量HFS在重负载下的性能,我们使用MOCK 对象去模仿一个2500个节点的集群和每个节点4个slots,运行100个job,每个job有1000个map任务和1000个reduce任务,这些任务放到20个pools中。在这样的工作负载下,在一个2.66 GHz Intel Core 2 Duo下,HFS每秒能够调度3200个任务。This is several times more than is needed to manage cluster of this size running reasonably-sized tasks(如果任务平均完成时间为10秒,在这里每秒仅能完成1000个任务)