大数据存储平台调优之Hadoop优化
在上节搭建完集群、完成Linux系统配置(优化)后以及建好HDFS上的目录后,我们接下来需要对Hadoop集群做一些优化的工作。我们从两个方面来说:一是HDFS存储方面,一是计算方面
1、 HDFS方面:
1> 存储格式的选择
对于分析类型的业务来说,最好的存储格式自然是列存储,因为数据量巨大,只扫关心的数据列无疑具有很大优势。目前hadoop生态中有两大列存储格式,一个是由Hortonworks和Microsoft开发的ORCFile,另一个是由Cloudera和Twitter开发的Parquet。Parquet是面向分析型业务的列式存储格式,最早由Twitter和Cloudera合作开发,2015年5月升级为Apache顶级项目
ORCFile是在RCFile的基础之上改造的。RCFile虽然号称列存储,但是只是“按列存储”而已,将数据先划分成row group,然后row group内部按照列进行存储。这其中没有列存储的一些关键特性,而这些特性在以前的列式数据库中早已用到。好在ORCFile已经弥补了这些特性,包括:
块过滤与块统计:每一列按照固定行数或大小进一步切分,对于切分出来的每一个数据单元,预先计算好这些单元的min/max/sum/count/null值,min/max用于在过滤数据的时候直接跳过数据单元,而所有这些统计值则可以在做聚合操作的时候直接采用,而不必解开这个数据单元做进一步的计算。
更高效的编码方式:RCFile中没有标注每一列的类型,事实上当知道数据类型时,可以采取特定的编码方式,本身就能很大程度上进行数据的压缩。常见的针对列存储的编码方式有RLE(大量重复数据),字典(字符串),位图(数字且基数不大)等等
Parquet的设计原理跟ORC类似,不过它有两个特点:
通用性:相比ORCFile专门给Hive使用而言,Parquet不仅仅是给Impala使用,还可以给其他查询工具使用,如Hive、Pig,进一步还能对接avro/thrift/pb等序列化格式。
基于Dremel思想的嵌套格式存储:关系数据库设计模式中反对存储复杂格式(违反第一范式),但是现在的大数据计算不仅出现了这种需求(半结构化数据),也能够高效的实现存储和查询效率,在语法上也有相应的支持(各种UDF,Hive的lateral view等)。Google Dremel就在实现层面做出了范例,Parquet则完全仿照了Dremel。
只读取需要的数据,跳过不符合条件的数据,降低I/O
压缩,一般采用snappy压缩
查询性能较xx提升30倍,存储空间节省70%
2> 传输使用AVRO
支持二进制编码、支持JSON编码
支持压缩
支持数据排序
序列化速度快
3>大量小文件处理
因为Namenode把文件系统的元数据放置在内存中,所以文件系统所能容纳的文件数目是由Namenode的内存大小来决定。当文件数到数十亿时,对于当前的硬件水平来说就没法实现了。还有一个问题就是,因为map task的数量是由splits来决定的,所以用MR处理大量的小文件时,就会产生过多的map task,我们知道在Hadoop中map任务或者reduce任务的都是进程方式启动的,启动进程本身需要花费几秒时间,因此针对小文件的改进策略,有几种方法:
· 利用Har等方式归档小文件,这个方法的原理就是把小文件归档起来管理。这种方法的缺点在于:如果想找回原来的小文件内容,那就必须事先保留原始小文件与归档文件的映射关系
· 既然1个Master有影响,那么能不能来个多Master设计,最新的Hadoop也有这方面的实现,但生产环境上使用这种方法的公司并不多,应慎用
4>fs.trash.interval:
dfs.blocksize:
dfs.namenode.handler.count:
二、计算方面的优化
计算方面通常涉及的是YARN和MapReduce的优化,YARN同时支持内存和CPU两种资源的调度,在YARN中,YARN通过分配Container来给每个应用提供处理能力, Container是YARN中处理能力的基本单位,是对内存,CPU等的封装。一个Container的内存是由java heap,jvm overhead和non java memory三部分构成的,如果用户为应用程序设置的内存大小为X GB(-xmxXg),jvm overhead为D GB。则ApplicationMaster为其申请的Container内存大小应为X+D,否则可能会因总内存超出限制被YARN杀死。通常经验是java heap的1.3倍左右是其Container的内存大小
在计算方面,我们还是从硬件资源开始,我们需要注意每台服务器上的内存大小、CPU核数、硬盘数。此外,我们还应该给操作系统预留一部分内存核核数来保证服务器或者其他进程(如HBase)所需的内存和核数,建议大家给服务器预留的内存大小如下:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
接下来我们需要确定每台机器可使用的最大容器个数,使用以下公式:
容器数(containers) = min(2 * CORES,1.8 * DISKS,(可用RAM总数)/ MIN_CONTAINER_SIZE)
MIN_CONTAINER_SIZE是最小容器大小(以RAM为单位)。该值取决于可用RAM的数量 - 在较小的存储器节点中,最小容器大小也应该更小。下表列出了推荐值:
|
|
|
|
|
|
|
|
|
|
最终计算是确定每个容器的RAM量:
每容器可用内存= max(MIN_CONTAINER_SIZE,(可用RAM总数)/容器))
通过这些计算,可以设置YARN和MapReduce配置:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
如果你觉得上述计算起来挺费劲的,hortonworks公司也提供了hdp-configuration-utils.py脚本帮大家计算(这个脚本已经集成在ambari里,改动的时候,ambari会给出可能会影响到的相关参数及值,非常贴心)。
python hdp-configuration-utils.py
参数说明:
|
|
|
|
|
|
|
|
|
|
举例说明:
pythonhdp-configuration-utils.py -c 32 -m 128 -d 11 -k False
Using cores=32 memory=128GB disks=11HBase=False
Profile: cores=32 memory=106496MBreserved=24GB usableMem=104GB disks=11
Num Container=20
Container Ram=5120MB
Used Ram=100GB
Unused Ram=24GB
yarn.scheduler.minimum-allocation-mb=5120
yarn.scheduler.maximum-allocation-mb=102400
yarn.nodemanager.resource.memory-mb=102400
mapreduce.map.memory.mb=5120
mapreduce.map.java.opts=-Xmx4096m
mapreduce.reduce.memory.mb=5120
mapreduce.reduce.java.opts=-Xmx4096m
yarn.app.mapreduce.am.resource.mb=5120
yarn.app.mapreduce.am.command-opts=-Xmx4096m
mapreduce.task.io.sort.mb=2048
需要特别指出的是每个容器将运行JVM以进行映射和减少任务。应将JVM堆大小(java.opts中的-Xmx)设置为低于容器(memory.mb)的值,以使其位于YARN分配的Container内存的边界内, java.opts一般设置为memory.mb的75%左右
举个列子在mapred-site.xml中:
mapreduce.map.memory.mb
4096
那么我们需要
mapreduce.map.java.opts
-Xmx3072m