Hadoop性能调优

互联网 来源:yzhang6_10 65℃ 0评论

Hadoop性能调优

1. 简介

Hadoop性能调优不仅涉及Hadoop本身的性能调优,还涉及更底层的硬件、操作系统和Java虚拟机等系统的调优。具体包括以下四部分,系统对这几部分适当地进行调优均可能给Hadoop带来性能提升。

Hadoop(JobTracker, TaskTracker,…)
Java Virtual Machine
Operating System(CentOS, RedHat)
Hardware(CPU, Memory, Network,…)

整体来说,提高作业运行效率需要Hadoop管理员和作业拥有者共同的努力,其中,管理员负责为用户提供一个高效的作业运行环境,而用户负责根据自己作业的特点让它尽可能快速地运行完成。

2. 从管理员角度进行调优

管理员负责为作业提供一个高效的运行环境。管理员需要从全局出发,通过调整一些关键参数值提高系统的吞吐率和性能。总体上看,管理员需要从硬件选择、操作系统参数调优、JVM参数调优和Hadoop参数调优等四个方面入手,为Hadoop用户提供一个高效的作业运行环境。

2.1 硬件选择 ####

Hadoop自身架构的基本特点决定了其硬件配置的选型。Hadoop采用了master/slave架构,其中,master(JobTracker或者NameNode)维护了全局元数据信息,重要性远远大于slave(TaskTracker或者DataNode)。在较低Hadoop版本中,master均存在单点故障问题,因此,master的配置应远远好于各个slave(TaskTracker或者DataNode)。

2.2 操作系统参数调优

因Hadoop自身一些特点,它只适合用于将Linux作为操作系统的生产环境。在实际应用场景中,管理员适当对Linux内核参数进行调优,可在一定程度上提高作业的运行效率,比较有用的调整选项如下:

  • 增大同时打开的文件描述符和网络连接上限
    在Hadoop集群中,由于涉及的作业和任务数目非常多,对于某个节点,由于操作系统内核在文件描述符和网络连接数目等方面的限制,大量的文件读写操作和网络连接可能导致作业运行事变,因此,管理员在启动Hadoop集群时,应使用ulimit命令将允许同时打开的文件描述符数目上限增大至一个合适的值,同时调整内核参数net.core.somaxconn至一个足够大的值。
    此外,Hadoop RPC采用了epoll作为高并发库,在使用时需适当调整epoll的文件描述符上限。
  • 关闭swap分区
    在Linux中,如果一个进程的内存空间不足,那么,它会将内存中的部分数据暂时写到磁盘上,当需要时,再将磁盘上的数据动态置换到内存中,通常而言,这种行为会大大降低进程的执行效率。在MapReduce分布式计算环境中,用户完全可以通过控制每个作业处理的数据量和每个任务运行过程中用到的各种缓冲区大小,避免使用swap分区。
  • 设置合理的预读取缓冲区大小
    磁盘I/O性能的发展远远滞后于CPU和内存,因而成为现代计算机系统的一个主要瓶颈。预读可以有效地减少磁盘的寻到次数和应用程序的I/O等待时间,是改进磁盘读I/O性能的重要优化手段之一。管理员可以使用Linux命令blockdev设置预读取缓冲区的大小,以提高Hadoop中大文件顺序读的性能。当然,也可以只为Hadoop系统本身增加预读缓冲区大小。
  • 文件系统选择与配置
    Hadoop的I/O性能很大程度上依赖于Linux本地文件系统的读写性能。Linux中有多种文件系统可提供选择,如ext3和ext4,不同的文件系统性能有一定的差别。
    在Linux文件系统中,当未启用noatime属性时,每个文件读操作会触发一个额外的文件写操作以记录文件最近访问时间。该日志操作可通过将其添加到mount属性中避免。
  • I/O调度器选择
    主流的Linux发型版自带了很多可供选择的I/O调度器。在数据密集型应用中,不同的I/O调度器性能表现差别较大,管理员可以根据自己的应用特点启动最合适的I/O调度器。

2.3 JVM参数调优

由于Hadoop中的每个服务和任务均会运行在一个单独的JVM中,因此,JVM的一些重要参数也会影响Hadoop性能。管理员可通过调整JVM FLAGS和JVM垃圾回收机制提高Hadoop性能。

2.4 Hadoop参数调优

  • 合理规划资源
    1)设置合理的槽位数目
    在Hadoop中,计算资源是用槽位(slot)表示的。slot分为两种:Map slot和Reduce slot。每种slot代表了一定量的资源,且同种slot是同质的,即同种slot代表的资源量是相同的。管理员需根据实际需要为TaskTracker配置一定数目的Map slot和Reduce slot数目,从而限制每个TaskTracker上并发执行的Map Task和Reduce Task数目。
    2)编写健康监测脚本
    Hadoop允许管理员为每个TaskTracker配置一个节点健康状况监测脚本。TaskTracker中包含一个专门的线程周期性执行该脚本,并将脚本执行结果通过心跳机制汇报给JobTrakcer。一旦JobTracker发现某个TaskTracker的当前状况为“不健康”(如内存或者CPU使用率过高),则会将其加入黑名单,从此不再为它分配新的任务(当前正在执行的任务仍会正常执行完毕),直到该脚本执行结果显示为“健康”。
  • 调整心跳配置
    1)调整心跳间隔
    TaskTracker与JobTracker之间的心跳间隔大小应该适度。如果太小,JobTracker需要处理高并发的心跳信息,势必造成不小的压力;如果太大,则空闲的资源不能及时通知JobTracker(进而为之分配新的Task),造成资源空闲,进而降低系统吞吐率。对于中小规模(300个节点以下)的Hadoop集群,缩短TaskTracker与JobTracker之间的心跳间隔可明显提高系统吞吐率。
    对于中心规模的Hadoop集群,3秒的心跳间隔过大,管理员可根据需要适当减小心跳间隔。
    2)启用带外心跳
    通常,心跳是由各个TaskTracker以固定时间间隔为周期发送给JobTracker的,心跳中包含节点资源使用情况、各任务运行状态等信息。心跳机制是典型的pull-based模型。TaskTracker周期性通过心跳向JobTracker汇报信息,同时获取新分配的任务。这种模型使得任务分配过程存在较大延时:当TaskTracker出现空闲资源时,它只能通过下一次心跳(对于不同规模的集群,心跳间隔不同,如1000个节点的集群,心跳间隔为10秒钟)告诉JobTracker,而不能立刻通知它。为了减少任务分配延迟,Hadoop引入了带外心跳(out-of-band heartbead)。带外心跳不同于常规心跳,它是任务运行结束或者任务运行失败时触发的,能够在出现空闲资源时第一时间通知JobTracker,以便它能够迅速为空闲资源分配新的任务。
  • 磁盘块配置
    Map Task中间结果要写到本地磁盘上,对于I/O密集型的任务来说,这部分数据会对本地磁盘造成很大压力,管理员可通过配置多块磁盘缓解写压力。当存在多块可用磁盘时,Hadoop将采用轮询的方式将不同Map Task的中间结果写到这些磁盘上,从而平摊负载。
  • 设置合理的RPC Handler和HTTP线程数目
    1)配置RPC Handler数目
    JobTracker需要并发处理来自各个TaskTracker的RPC请求,管理员可根据集群规模和服务器并发处理能够调整RPC Handler数目,以使JobTracker服务能力最佳。
    2)配置HTTP线程数目
    在Shuffle阶段,Reduce Task通过HTTP请求从各个TaskTracker上读取Map Task中间结果,而每个TaskTracker通过Jetty Server处理这些HTTP请求。管理员可以适当调整Jetty Server的工作线程数以提高Jetty Server的并发处理能力。
  • 慎用黑名单机制
    当一个作业运行结束时,它会统计在各个TaskTracker上失败的任务数目。如果一个TaskTracker被一定数目的作业加入黑名单,则JobTracker会将该TaskTracker加入系统黑名单,此后JobTracker不再为其分配新的任务,直到一定时间段内没有出现失败的任务。
    当Hadoop集群规模较小时,如果一定数量的节点被频繁加入系统黑名单中,则会大大降低集群吞吐率和计算能力。
  • 启用批量任务调度
    在Hadoop中,调度器是最核心的组件之一,它负责将系统中空闲的资源分配给各个任务。当前Hadoop提供了多种调度器,包括默认的FIFO调度器、Fair Scheduler、Capacity Scheduler等,调度器的调度效率直接决定了系统的吞吐率高低。通常,为了将空闲资源尽可能分配给任务,Hadoop调度器均支持批量任务调度,即一次将所有空闲任务分配下去,而不是一次只分配一个。
  • 选择合适的压缩算法
    Hadoop通常用于处理I/O密集型应用。对于这样的应用,Map Task会输出大量中间数据,这些数据的读写对用户是透明的,如果能够支持中间数据压缩存储,则会明显提升系统的I/O性能。当选择压缩算法时,需要考虑压缩比和压缩效率两个因素。有的压缩算法有很好的压缩比,但压缩/解压缩效率很低;反之,一些算法的压缩/解压缩效率很高,但压缩比很低。故一个优秀的压缩算法需平衡压缩比和压缩效率两个因素。
    当前有多种可选的压缩格式,如gzip、zip、bzip、LZO、Snappy等,其中LZO和Snappy在压缩比和压缩效率两方面的表现都比较优秀。Snappy是Google开源的数据压缩库,它的编码/解码器已经内置到Hadoop1.0以后的版本中;LZO则不同,它是基于GPL许可的,不能通过Apache来分发许可,故它的Hadoop编码/解码器必须单独下载。
  • 启动预读取机制
    预读取机制可以有效提高磁盘的I/O读性能。而Hadoop是典型的顺序读系统,采用预读取机制可明显提高HDFS读性能和MapReduce作业执行效率。管理员可为MapReduce的数据拷贝和IFile文件读取启用预读取功能。

3. 从用户角度进行优化

Hadoop为用户作业提供了多种可配置的参数,以允许用户根据作业特点调整这些参数值使作业运行效率达到最优。

3.1 应用程序编写规范

  • 设置Combiner
    对于一大批MapReduce应用程序,如果可以设置一个Combiner,那么对于提高作业性能十分有帮助。Combiner可减少Map Task中间输出结果,从而减少各个Reduce Task的远程拷贝数据量,最终表现为Map Task和Reduce Task执行时间缩短。
  • 选择合理的Writable类型
    在MapReduce模型中,Map Task和Reduce Task的输入和输出数据类型为Writable。Hadoop本身已经提供了很多Writable实现,包括IntWritable,FloatWritable。为应用程序处理的数据类型选择合适的Writable类型可大大提升性能。如处理整型数据时,直接采用IntWritable比先以Text类型读入再转换成整型要高效。如果输出的整型大部分可用一个或者两个字节保存,那么可直接采用VIntWritable或者VLongWritable。他们采用了变长整型编码方式,可大大减少输出数据量。

3.2 作业级别参数调优

  • 规划合理的任务数目
    一个作业的任务数目对作业运行时间有重要的影响。如果一个作业的任务数目过多(即每个任务处理数据很少,执行时间很短),则任务启动时间所占比例将会大大增加,反之,如果一个作业的任务数目过少(即每个任务处理数据很多,执行时间很长),则可能会产生过多的溢写数据影响任务执行性能,且任务失败后重新计算代价过大。
    在Hadoop中,每个Map Task处理一个Input Split。Input Split的划分方式是由用户定义的InputFormat决定的。
    对于Reduce Task而言,每个作业的Reduce Task数目通常由用户决定。用户可根据估算的Map Task输出数据量设置Reduce Task数目,以防止每个Reduce Task处理的数据量过大造成大量写磁盘操作。
  • 增加输入文件副本数
    如果一个作业并行执行的任务数量非常多,那么这些任务共同的输入文件可能成为瓶颈。为防止多个任务并行读取一个文件内容造成瓶颈,用户可根据需要增加输入文件的副本数目。用户可通过在客户端配置文件hdfs-site.xml中增加相应的配置选项,从而修改文件副本数目。
  • 启动推测执行机制
    推测执行是Hadoop对“拖后腿”任务的一种优化机制。当一个作业的某些任务运行速度明显慢于同作业的其他任务时,Hadoop会在另一个节点上为“慢任务”启动一个备份任务,这样,两个任务同时处理一份数据,而Hadoop最终会将优先完成的那个任务的结果作为最终结果,并将另外一个任务杀掉。
  • 设置失败容忍度
    Hadoop允许设置作业级别和任务级别的失败容忍度。作业级别的失败容忍是指Hadoop允许每个作业有一定比例的任务运行失败,这部分任务对应的输入数据将被忽略;任务级别的失败容忍是指Hadoop允许任务运行失败后再次在另外节点上尝试运行,如果一个任务经过若干次尝试运行后仍然运行失败,那么Hadoop才会最终认为该任务运行失败。
    用户应根据应用程序的特点设置合理的失败容忍度,以尽快让作业运行完成和避免没必要的资源浪费。
  • 适当打开JVM重用功能
    为了实现任务隔离,Hadoop将每个任务放到一个单独的JVM中执行,而对于执行时间较短的任务,JVM启动和关闭将占用很大比例的时间,为此,用户可启动JVM重用功能,这样,一个JVM可连续启动多个同类型任务。
  • 设置任务超时时间
    在一些特殊情况下,一个任务可能因为某种原因(如Bug)阻塞了,这会拖慢整个作业的执行进度,甚至可能导致作业无法运行结束。针对此情况,Hadoop增加了任务超时机制。如果一个任务在一定时间间隔内没有汇报进度,则TaskTracker会主动将其杀死,从而在另一个节点上重新启动执行。
    用户可根据实际需要配置任务超时时间。
  • 合理使用DistributedCache
    当用户的应用程序需要一个外部文件(如字典、配置文件等)时,通常需要使用DistributedCache将文件分发到各个节点上。一般情况下,得到外部文件有两种方法:一种是外部文件与应用程序jar包一起放到客户端,当提交作业时由客户端上传到HDFS的一个目录下,然后通过DistributedCache分发到各个节点上;另外一种方法是事先将外部文件直接放到HDFS上。从效率上讲,第二种方法比第一种更高效。第二种方式不仅节省了客户端上传文件的时间,还隐含着告诉DistributedCache:“请将文件下载到各节点的public级别(而不是private级别)共享目录中”,这样,后续所有的作业可重用已经下载好的文件,不必重复下载,即“一次下载,终生受益”。
  • 合理控制Reduce Task的启动时机
    在MapReduce计算模型中,由于Reduce Task依赖于Map Task的执行结果,因此,从运算逻辑上讲,Reduce Task应晚于Map Task启动。在Hadoop中,合理控制Reduce Task启动时机不仅可以加快作业运行速度,而且可提高系统资源利用率。如果Reduce Task启动过早,则可能由于Reduce Task长时间占用Reduce slot资源造成“slot Hoarding”现象,从而降低资源利用率;反之,如果Reduce Task启动过晚,则会导致Reduce Task获取资源延迟,增加作业运行时间。
  • 跳过坏记录
    Hadoop是用于处理海量数据的,对于大部分数据密集型应用而言,丢弃一条或者几条数据对最终结果的影响并不大,因此,Hadoop为用户提供了跳过坏记录的功能。当一套或者几条坏数据记录导致任务运行失败时,Hadoop可自动识别并跳过这些坏记录。
  • 提高作业优先级
    所有Hadoop作业调度器进行任务调度时均会考虑作业优先级这一因素。一个作业的优先级越高,它能够获取的资源(指slot数目)也越多。注意:在生产环境中,管理员已经按照作业重要程度对作业进行分级,不同重要程度的作业允许配置的优先级不同,用户不可以擅自进行调整。Hadoop提供了5种作业优先级,分别是VERY_HIGH、HIGH、NORMAL、LOW和VERY_LOW。

3.3 任务级别参数调优

  • Map Task调优
    Map Task的输出结果被暂时存放在一个环形缓冲区汇总,这个缓冲区的大小由参数“io.sort.mb”指定(单位是MB,默认是100MB)。该缓冲区主要由两部分组成:索引和实际数据。默认情况下,索引占整个buffer的比例为io.sort.record.percent(默认为0.05,即5%),剩下的空间全部存放数据,当且仅当满足以下任意一个条件时,才会触发一次flush,生成一个临时文件。
    • 索引空间使用率达到比例为io.sort.spill.percent(默认是0.8,即80%)。
    • 数据空间使用率达到比例为io.sort.spill.percent(默认是0.8,即80%)。
      合理地调整io.sort.record.percent值,可减少中间文件数目,提高任务执行效率。如,若key/value非常小,则可以适当调大io.sort.percent值,以防止索引空间优先达到使用上限触发flush。考虑到每条数据记录(一个key/value)需占用索引大小为16B,故建议io.sort.record.percent=16/(16+R),其中R为平均每天记录的长度。
      综上,用户可根据自己作业的特点对以下参数进行调优:

      io.sort.mb;
      io.sort.record.percent;
      io.sort.spill.percent。

  • Reduce Task调优
    Reduce Task会启动多个拷贝线程从每个Map Task上读取相应的中间结果。对每个待拷贝的文件,如果文件大小小于一定阈值A,则将其放到内存中,否则以文件的形式存放到磁盘上。如果内存中文件满足一定条件D,则会将这些数据写入磁盘,而当磁盘上文件数目达到io.sort.factor(默认是10)时,进行一次合并。阈值A为:
    heapsize*{maored.job.shuffle.input.buffer.percent}*0.25
    其中,heapsize是通过参数“mapred.child.java.opts”指定的,默认是200MB;mapred.job.shuffle.input.buffer.percent默认大小为0.7。
    条件D为以下两个条件中任意一个:

    内存使用率(总的可用内存为headsize*{mapred.job.shuffle.input.buffer.percent})达到mapred.job.shuffle.merge.percent(默认是0.66)。
    内存中文件数目超过mapred.inmem.merge.threshold(默认是1000)。

    综上所述,用户可根据自己作业的特点对以下参数进行调优:

    mapred.reduce.parallel.copies;
    io.sort.factor;
    mapred.child.java.opts;
    mapred.job.shuffle.input.buffer.percent;
    mapperd.inmem.merge.threshold。

3.4 总结

Hadoop性能调优不仅涉及到Hadoop本身的性能调优,还涉及更底层的硬件、操作系统和Java虚拟机等系统的调优。


  • 参考文献:Hadoop技术内幕