hadoop的性能调优是个比较艰难的事情,由于这个系统的整个环境比较复杂,对于接触时间不长的人来说,配置都很难,更别说找出性能优化的点了。
性能优化涉及的方面很广,操作系统,网络配置,配置文件,调度器等等,抓出几点来说,但不敢说这几点就是别人所遇到的性能瓶颈,抛砖引玉而已。应用场景不同,优化配置肯定是各不相同的。
对于操作系统和网络环境的调优,这个需要讲的东西就太多了,无法在一篇文章里赘述。集中于几个关键词:sysctl,ulimit,hosts文件,内网配置。
尽量把hadoop集群配置在内网地址上,这就不用多说了吧。
下面主要探讨hadoop的配置文件和调度器的选择和开发。
以我公司的hadoop集群举例来说,主要是用了数据压缩和索引和对调度器策略的优化。
使用压缩是一个不错的选择,比如我们自己的集群用的是LZO的压缩方式,压缩比大概是原始数据的1/3,也就是说,1G的原始日志大概能压缩成300Mb左右,一方面压缩比不错,另一方面,读取速度也很不错,配合的是Native的lzo库。一个叫hadoop-gpl的东西。前一阵子泰国水灾,硬盘难买,以压缩的方式也可以多撑一阵子。
如果给lzo建立索引,效果就更好了
当然你需要先安装hadoopgpl。
core-site.xml
<property>
<name>io.compression.codecs</name>
<value>org.apache.hadoop.io.compress.DefaultCodec,com.hadoop.compression.lzo.LzoCodec,com.hadoop.compression.lzo.LzopCodec,org.apache.hadoop.io.compress.GzipCodec,org.apache
.hadoop.io.compress.BZip2Codec</value>
</property>
<property>
<name>io.compression.codec.lzo.class</name>
<value>com.hadoop.compression.lzo.LzoCodec</value>
</property>
mapred-site.xml
<property>
<name>mapred.compress.map.output</name>
<value>true</value>
</property>
<property>
<name>mapred.map.output.compression.codec</name>
<value>com.hadoop.compression.lzo.LzoCodec</value>
</property>
<property>
<name>mapred.child.java.opts</name>
<value>-Djava.library.path=/opt/hadoopgpl/native/Linux-amd64-64</value>
</property>
当然每台服务器都需要定义这个才可以。
还有一个很重要的优化是槽位的设置和调度器的选择,这个直接关系到hadoop的计算能力。相同硬件情况下,配置好的集群的在计算相同任务的情况下,要比配置糟糕的集群快几倍乃至几十倍。
对于map/reduce槽位的配置还有job对java虚拟机的配置,我目前总结的规律大概是这样,namenode的槽位总数相加和等于CPU数量,同时map槽位数大概是reduce槽位的3倍,也就是这样,如果你有一个8核的服务器,map数量就应该是6,reduce数量是2。对于datanode,我们需要他的计算能力强一些,就把map和reduce槽位总和设置成cpu数量的2倍,同时map数是reduce数量的3倍,同样是8核的datanode,map数就是12,reduce数就是4。对于内存的使用,还是拿配置文件举例说明吧。
mapred-site on namenode:
<property>
<name>mapred.tasktracker.map.tasks.maximum</name>
<value>6</value>
<final>true</final>
</property>
<property>
<name>mapred.tasktracker.reduce.tasks.maximum</name>
<value>2</value>
<final>true</final>
</property>
<property>
<name>mapred.child.java.opts</name>
<value>-Xmx1536M</value>
</property>
mapred-site on datanode:
<property>
<name>mapred.tasktracker.map.tasks.maximum</name>
<value>12</value>
<final>true</final>
</property>
<property>
<name>mapred.tasktracker.reduce.tasks.maximum</name>
<value>4</value>
<final>true</final>
</property>
<property>
<name>mapred.map.child.java.opts</name>
<value>-Xmx1224M</value>
</property>
<property>
<name>mapred.reduce.child.java.opts</name>
<value>-Xmx2048M</value>
</property>
对于map槽位的内存占用,我的理解是这样,内存总数/CPU核数/4,上下可以浮动几百兆。
对于reduce槽位是内存总数/cpu核数/2。
然后简单说下调度器的问题,hadoop默认的调度器是FIFO,就是先入先出,通常来说,这就比较够用了。但是如果集群规模较小,计算任务又比较多,还需要细分不同任务的槽位分配,就还是配置其他的调度器比较好。
常用的有两种第三方调度器,yahoo开发的Capacity Scheduler和Facebook贡献的Fair Scheduler。翻译过来叫计算能力调度器和公平调度器,可能大家听公平调度器听的比较多,不过目前我们公司主要是用计算能力调度器。
因为配置的XML太长,我就不贴了,需要了解计算能力调度器的配置方法,可以访问我的同事老赵的技术博客。
在我们的应用场景里,计算能力被分为了3类,每个分类的map/reudce槽位数是不同的,根据统计平时的计算量来固定分配的槽位数。default,rush,和hive,其中普通的streaming的计算方式放入default的分类中执行,日志清洗和入库单独使用rush分类,hive,顾名思义,就是给hive数据库单独使用的。这个分配的map/reduce是最多的。平时定时任务的70%左右都是用hive跑的,临时数据查询95%依赖hive。
这样做的好处是计算任务的计算能力被隔离,互不干扰。可根据业务需求进行分类。避免任务抢占造成的资源大量消耗。