一、 Hadoop概述
随着企业要处理的数据量越来越大,MapReduce思想越来越受到重视。Hadoop是MapReduce的一个开源实现,由于其良好的扩展性和容错性,已得到越来越广泛的应用。
Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简HDFS。HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。
Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提供了计算。Hadoop的架构图如下所示:
Hadoop作为一个基础大数据处理平台,虽然其应用价值已得到大家认可,但仍存在很多问题,以下是主要几个:
1、Namenode/jobtracker单点故障。
Hadoop采用的是master/slaves架构,该架构管理起来比较简单,但存在致命的单点故障和空间容量不足等缺点,这已经严重影响了Hadoop的可扩展性。
2、HDFS小文件问题。
在HDFS中,任何block,文件或者目录在内存中均以对象的形式存储,每个对象约占150byte,如果有1000 0000个小文件,每个文件占用一个block,则namenode需要2G空间。如果存储1亿个文件,则namenode需要20G空间。这样namenode内存容量严重制约了集群的扩展。
3、jobtracker同时进行监控和调度,负载过大。
为了解决该问题,yahoo已经开始着手设计下一代Hadoop MapReduce(见参考资料1)。他们的主要思路是将监控和调度分离,独立出一个专门的组件进行监控,而jobtracker只负责总体调度,至于局部调度,交给作业所在的client。
4、数据处理性能。
很多实验表明,其处理性能有很大的提升空间。Hadoop类似于数据库,可能需要专门的优化工程师根据实际的应用需要对Hadoop进行调优,有人称之为“Hadoop Performance Optimization” (HPO)。
为了提高其数据性能,很多人开始优化Hadoop。总结看来,对于Hadoop,当前主要有几个优化思路:
(1) 操作系统调优
Hadoop的运行环境,硬件配置起得至关重要的作用,硬件的参数配置对性能影响非常大,在部署Hadoop时,合理的硬件选择是一种优化思路。
(2) 从应用程序角度进行优化。
由于mapreduce是迭代逐行解析数据文件的,怎样在迭代的情况下,编写高效率的应用程序,是一种优化思路。
(3) 对Hadoop参数进行调优。
当前hadoop系统有190多个配置参数,怎样调整这些参数,使hadoop作业运行尽可能的快,也是一种优化思路。
(4) 从系统实现角度进行优化。
这种优化难度是最大的,它是从hadoop实现机制角度,发现当前Hadoop设计和实现上的缺点,然后进行源码级地修改。该方法虽难度大,但往往效果明显。
二、 运行环境
原则一: 主节点可靠性要好于从节点。
原则二:多路多核,高频率cpu、大内存。
比如,NameNode节点中100万文件的元数据要消耗800M内存,内存决定了集群保存文件数的总量,ResourceManager同时运行的作业会消耗一定的内存。
DataNode的内存需要根据cpu的虚拟核数(vcore) 进行配比,CPU的vcore数计算公式为=cpu个数 * 单cpu核数* HT(超线程)
内存容量大小 = vcore数 * 2GB(至少2GB)
原则三: 根据数据量确定集群规模
一天增加10GB, 365天,原数据1TB,replacation=3, 1.5个mapreduce 计算完保存的数据,规划容量
(1TB + 10GB*365)*3*1.5 =20.53TB
如果一台datanode的存储空间为2TB,21/2=11,总节点为 = 11+2 =13
还要考虑作业并不是均匀分布的, 有可能会倾斜到某一个时间段,需要预留资源。
原则四: 不要让网路I/O 成为瓶颈
hadoop 作业通常是 I/O密集型而非计算密集型, 瓶颈通常集中出现在I/O上, 计算能力可以通过增加新节点进行线性扩展,要注意网络设别处理能力。
三、 操作系统调优
(1) 避免使用swap 分区 将hadoop守护进程的数据交换到硬盘的行为可能会导致操作超时。
(2) 调整内存分配策略
操纵系统内核根据vm.oversommit_memory的值来决定分配策略,并且通过vm.overcommit_ratio的值来设定超过物理内存的比例。
(3) 修改net.core.somaxconn参数
该参数表示socker监听backlog的上限,默认为128,socker的服务器会一次性处理backlog中的所有请求,hadoop的ipc.server.listen.queue.size参数和linux的net.core.somaxconn
参数控制了监听队列的长度,需要调大。
(4) 增大同时打开文件描述符的上限
对内核来说,所有打开的文件都通过文件描述符引用,文件描述符是一个非负整数,hadoop的作业经常会读写大量文件,需要增大同时打开文件描述符的上限。
(5)选择合适的文件系统,并禁用文件的访问时间
ext4 xfs ,文件访问时间可以让用户知道那些文件近期被查看或修改, 但对hdfs来说,获取某个文件的某个块 被修改过,没有意义,可以禁用。
(6)关闭THP (transparent Huge Pages)
THP 是一个使管理 Huge Pages自动化的抽象层, 它会引起cpu占用率增大, 需要关闭。
echo never >/sys/kernel/mm/redhat_transparent_hugepage/defrag
echo never >/sys/kernel/mm/redhat_transparent_hugepage/enabled
echo never >/sys/kernel/mm/transparent_hugepage/enabled
echo never >/sys/kernel/mm/transparent_hugepage/defrag
四、 应用角度进行调优
(1)避免不必要的Reduce任务
如果要处理的数据是排序且已经分区的,或者对于一份数据, 需要多次处理, 可以先排序分区;然后自定义InputSplit, 将单个分区作为单个mapred的输入;在map中处理数据, Reducer设置为空。这样, 既重用了已有的 “排序”, 也避免了多余的reduce任务。
(2)外部文件引入
有些应用程序要使用外部文件,如字典,配置文件等,这些文件需要在所有task之间共享,可以放到分布式缓存DistributedCache中(或直接采用-files选项,机制相同)。更多的这方面的优化方法,还需要在实践中不断积累。
(3) 为job添加一个Combiner
为job添加一个combiner可以大大减少shuffle阶段从map task拷贝给远程reduce task的数据量。一般而言,combiner与reducer相同。
(4) 根据处理数据特征使用最适合和简洁的Writable类型
Text对象使用起来很方便,但它在由数值转换到文本或是由UTF8字符串转换到文本时都是低效的,且会消耗大量的CPU时间。当处理那些非文本的数据时,可以使用二进制的Writable类型,如IntWritable, FloatWritable等。二进制writable好处:避免文件转换的消耗;使map task中间结果占用更少的空间。
(5)重用Writable类型
很多MapReduce用户常犯的一个错误是,在一个map/reduce方法中为每个输出都创建Writable对象。
例如,你的Wordcoutmapper方法可能这样写:
public voidmap(LongWritable key, Text value, Context context) {
for (String word : words) {
output.collect(new Text(word), newIntWritable(1));
}
}
这样会导致程序分配出成千上万个短周期的对象。Java垃圾收集器就要为此做很多的工作。
更有效的写法是:
class WordCountMapperextends Mapper<LongWritable, Text, Text, IntWritable>{
Text wordText = new Text();
IntWritable one = new IntWritable(1);
public void map(LongWritable key, Textvalue, Context context) {
for (String word: words) {
wordText.set(word);
output.collect(wordText,one);
}
}
}
(6)使用StringBuffer而不是String
当需要对字符串进行操作时,使用StringBuffer而不是String,String是read-only的,如果对它进行修改,会产生临时对象,而StringBuffer是可修改的,不会产生临时对象。
(7) 调试
最重要,也是最基本的,是要掌握MapReduce程序调试方法,跟踪程序的瓶颈。
具体可参考:
http://www.cloudera.com/blog/2009/12/7-tips-for-improving-mapreduce-performance/
五、 Hadoop参数调优
5.1 Linux文件系统参数调整
(1) noatime 和 nodiratime属性
文件挂载时设置这两个属性可以明显提高性能。。默认情况下,Linuxext2/ext3 文件系统在文件被访问、创建、修改时会记录下文件的时间戳,比如:文件创建时间、最近一次修改时间和最近一次访问时间。如果系统运行时要访问大量文件,关闭这些操作,可提升文件系统的性能。Linux 提供了 noatime 这个参数来禁止记录最近一次访问时间戳。
(2) readahead buffer
调整linux文件系统中预读缓冲区地大小,可以明显提高顺序读文件的性能。默认buffer大小为256 sectors,可以增大为1024或者2408 sectors(注意,并不是越大越好)。可使用blockdev命令进行调整。
(3) 避免RAID和LVM操作
避免在TaskTracker和DataNode的机器上执行RAID和LVM操作,这通常会降低性能。
5.2 Hadoop通用参数调整
(1) dfs.namenode.handler.count或mapred.job.tracker.handler.count
namenode或者jobtracker中用于处理RPC的线程数,默认是10,较大集群,可调大些,比如50。
(2) dfs.datanode.handler.count
datanode上用于处理RPC的线程数。默认为3,较大集群,可适当调大些,比如8。需要注意的是,每添加一个线程,需要的内存增加。
(3) tasktracker.http.threads
HTTP server上的线程数。运行在每个TaskTracker上,用于处理map task输出。大集群,可以将其设为40~50。
5.3 HDFS相关配置
(1) dfs.replication
文件副本数,通常设为3,不推荐修改。
(2) dfs.block.size
HDFS中数据block大小,默认为128M,对于较大集群,可设为256MB或者512MB。(也可以通过参数mapred.min.split.size配置)
(3) mapred.local.dir和dfs.data.dir
这两个参数mapred.local.dir和dfs.data.dir 配置的值应当是分布在各个磁盘上目录,这样可以充分利用节点的IO读写能力。运行 Linux sysstat包下的iostat -dx 5命令可以让每个磁盘都显示它的利用率。
5.4 map/reduce 相关配置
(1) {map/reduce}.tasks.maximum
同时运行在TaskTracker上的最大map/reduce task数,一般设为(core_per_node)/2~2*(cores_per_node)。
(2) io.sort.factor
当一个map task执行完之后,本地磁盘上(mapred.local.dir)有若干个spill文件,map task最后做的一件事就是执行merge sort,把这些spill文件合成一个文件(partition)。执行merge sort的时候,每次同时打开多少个spill文件由该参数决定。打开的文件越多,不一定merge sort就越快,所以要根据数据情况适当的调整。
(3) mapred.child.java.opts
设置JVM堆的最大可用内存,需从应用程序角度进行配置。
5..5 map task相关配置
(1) io.sort.mb
Map task的输出结果和元数据在内存中所占的buffer总大小。默认为100M,对于大集群,可设为200M。当buffer达到一定阈值,会启动一个后台线程来对buffer的内容进行排序,然后写入本地磁盘(一个spill文件)。
(2) io.sort.spill.percent
这个值就是上述buffer的阈值,默认是0.8,即80%,当buffer中的数据达到这个阈值,后台线程会起来对buffer中已有的数据进行排序,然后写入磁盘。
(3) io.sort.record
Io.sort.mb中分配给元数据的内存百分比,默认是0.05。这个需要根据应用程序进行调整。
(4)mapred.compress.map.output/Mapred.output.compress
中间结果和最终结果是否要进行压缩,如果是,指定压缩方式(Mapred.compress.map.output.codec/Mapred.output.compress.codec)。推荐使用LZO压缩。Intel内部测试表明,相比未压缩,使用LZO压缩的TeraSort作业运行时间减少60%,且明显快于Zlib压缩。
5.6 reduce task相关配置
(1) Mapred.reduce.parallel
Reduce shuffle阶段copier线程数。默认是5,对于较大集群,可调整为16~25。
5.7 YARN调优
Yarn的资源表示模型为ceontainer(容器),container 将资源抽象为两个维度,内存和虚拟cpu(vcore)
1. 兼容各种计算框架
2. 动态分配资源,减少资源浪费
容器内存
yarn.nodemanager.resource.memory-mb
最小容器内存
yarn.scheduler.minimum-allocation-mb
容器内存增量
yarn.scheduler.increment-allocation-mb
最大容器内存
yarn.scheduler.maximum-allocation-mb
容器虚拟cpu内核
yarn.nodemanager.resource.cpu-vcores
最小容器虚拟cpu内核数量
yarn.scheduler.minimum-allocation-vcores
容器虚拟cpu内核增量
yarn.scheduler.increment-allocation-vcores
最大容器虚拟cpu内核数量
yarn.scheduler.maximum-allocation-vcores
MapReduce调优,调优三大原则
1.增大作业并行程度
2.给每个任务足够的资源
3. 在满足前2个条件下,尽可能的给shuffle预留资源
六、 从系统实现角度进行优化
6.1 在可移植性和性能之间进行权衡
(1) 调度延迟
Hadoop采用的是动态调度算法,即:当某个tasktracker上出现空slot时,它会通过HEARBEAT(默认时间间隔为3s,当集群变大时,会适当调大)告诉jobtracker,之后jobtracker采用某种调度策略从待选task中选择一个,再通过HEARBEAT告诉tasktracker。从整个过程看,HDFS在获取下一个task之前,一直处于等待状态,这造成了资源利用率不高。此外,由于tasktracker获取新task后,其数据读取过程是完全串行化的,即:tasktracker获取task后,依次连接namenode,连接datanode并读取数据,处理数据。在此过程中,当tasktracker连接namenode和datanode时,HDFS仍在处于等待状态。
为了解决调度延迟问题,可以考虑的解决方案有:重叠I/O和CPU阶段(pipelining),task预取(task prefetching),数据预取(data prefetching)等
(2) 可移植性假设
为了增加Hadoop的可移植性,它采用java语言编写,这实际上也潜在的造成了HDFS低效。Java尽管可以让Hadoop的可移植性增强,但是它屏蔽了底层文件系统,这使它没法利用一些底层的API对数据存储和读写进行优化。首先,在共享集群环境下,大量并发读写会增加随机寻道,这大大降低读写效率;另外,并发写会增加磁盘碎片,这将增加读取代价(HDFS适合文件顺序读取)。
为了解决该问题,可以考虑的解决方案有:修改tasktracker上的线程模型,现在Hadoop上的采用的模型是one thread per client,即每个client连接由一个线程处理(包括接受请求,处理请求,返回结果);修改之后,可将线程分成两组,一组用于处理client通信(Client Thread),一组用于存取数据(Disk Threads,可采用one thread per disk)。
6.2 Prefetching与preshuffling
(1) PreFetching
preFetching包括Block-intra prefetching和Block-interprefetching:
Block-intraPrefetching对block内部数据处理方式进行优化。采用的策略是以双向处理(bi-directional processing)方式提升效率,即一端进行计算,一端预取将要用到的数据(同步机制)。
需解决两个问题,一是计算和预取同步。借用进度条(processing bar)的概念,进度条监控两端的进度,当同步将被打破时,调用一个信号。二是确定合适的预取率。通过实验发现,预取数据量并不是越多越好。采用重复实验的方法确定预取数据率。
Block-interPrefetching在block层面预取数据。当某个task正在处理数据块A1时,预测器预测它接下来要处理的数据块,假设是A2,A3,A4,则将这几个数据块读到task所在的rack上,这样加快了task接下来数据读取速度。
(2) PreShuffling
数据被map task处理之前,由预测器判断每条记录将要被哪个reduce task处理,将这些数据交由靠近该reduce task的节点上的map task处理。
主页:http://incubator.apache.org/projects/hama.html
6.3 Five Factors
影响Hadoop性能的因素,分别为计算模型,I/O模型,数据解析,索引和调度,同时针对这5个因素提高了相应的提高性能的方法,最后实验证明,通过这些方法可以将Hadoop性能提高2.5到3.5倍。
(1) 计算模型
在Hadoop中,map task产生的中间结果经过sort-merge策略处理后交给reduce task。而这种处理策略(指sort-merge)不能够定制,这对于有些应用而言(有些应用程序可能不需要排序处理),性能不佳。此外,即使是需要排序归并处理的,sort-merge也并不是最好的策略。
(2) I/O模型
Reader可以采用两种方式从底层的存储系统中读取数据:direct I/O和streaming I/O。direct I/O是指reader直接从本地文件中读取数据;streaming I/O指使用某种进程间通信方式(如TCP或者JDBC)从另外一个进程中获取数据。从性能角度考虑,direct I/O性能更高,各种数据库系统都是采用direct I/O模式。但从存储独立性考虑,streaming I/O使Hadoop能够从任何进程获取数据,如datanode或database,此外,如果reader不得不从远程节点上读取数据,streaming I/O是仅有的选择。
(3) 数据解析
在hadoop中,原始数据要被转换成key/value的形式以便进一步处理,这就是数据解析。现在有两种数据解析方法:immutabledecoding and mutable decoding。Hadoop是采用java语言编写的,java中很多对象是immutable,如String。当用户试图修改一个String内容时,原始对象会被丢弃而新对象会被创建以存储新内容。在Hadoop中,采用了immutable对象存储字符串,这样每解析一个record就会创建一个新的对象,这就导致了性能低下。
(4) 索引
HDFS设计初衷是处理无结构化数据,既然这样,怎么可能为数据添加索引。实际上,考虑到以下几个因素,仍可以给数据添加索引:
A、hadoop提供了结构将数据记录解析成key/value对,这样也许可以给key添加索引。
B、如果作业的输入是一系列索引文件,可以实现一个新的reader高效处理这些文件。
(5) 调度
Hadoop采用的是动态调度策略,即每次调度一个task运行,这样会带来部分开销。而database采用的静态调度的策略,即在编译的时候就确定了调度方案。当用户提交一个sql时,优化器会生成一个分布式查询计划交给每一个节点进行处理。
总结
本文档介绍Hadoop现有的优化点,总体来说,对于Hadoop平台,现在主要有三种优化思路,分别为:从应用程序角度角度进行优化;从参数配置角度进行优化;从系统实现角度进行优化。对于第一种思路,需要根据具体应用需求而定,同时也需要在长期实践中积累和总结;对于第二种思路,大部分采用的方法是根据自己集群硬件和具体应用调整参数,找到一个最优的。对于第三种思路,难度较大,但效果往往非常明显,总结这方面的优化思路,主要有以下几个:
(1) 对namenode进行优化,包括增加其吞吐率和解决其单点故障问题。当前主要解决方案有3种:分布式namenode,namenode热备和zookeeper。
(2)HDFS小文件问题。当Hadoop中存储大量小文件时,namenode扩展性和性能受到极大制约。现在Hadoop中已有的解决方案包括:Hadoop Archive,Sequence file和CombineFileInputFormat。
(3)调度框架优化。在Hadoop中,每当出现一个空闲slot后,tasktracker都需要通过HEARBEAT向jobtracker所要task,这个过程的延迟比较大。可以用task预调度的策略解决该问题。
(4)共享环境下的文件并发存取。在共享环境下,HDFS的随机寻道次数增加,这大大降低了文件存取效率。可以通过优化磁盘调度策略的方法改进。
(5) 索引。索引可以大大提高数据读取效率,如果能根据实际应用需求,为HDFS上的数据添加索引,将大大提高效率。
参考资料:
http://www.cnblogs.com/fengjian2016/p/6214301.html
http://dongxicheng.org/mapreduce/hadoop-optimization-0/
http://dongxicheng.org/mapreduce/hadoop-optimization-1/