201707052352复习-hadoop篇3:mr程序和yarn的执行过程

hadoop集群配置有三种:
1、一个namenode,多个datanode
2、两个namenode,一个是主,一个是备,高可靠,多个datanode
3、两个namenode,federation集群,没有主备之分,同时工作,只是因为一个namenode的内不能不够大,扩容namenode。

------------第一种--------------
hadoop-env.sh 配置hadoop的整体大环境,只需要配置JAVA_HOME即可,大约25行左右。
core-site.xml hadoop核心配置文件,需要配置hadoop使用什么样的文件系统,
<property>
<name>fs.defaultFS</name> 设置默认的文件系统
<value>hdfs://dwh:9000</value>文件系统是hdfs,访问uri的主机名是dwh,端口号是9000 ,即namenode的机器
</property>
<property>
<name>hadoop.tmp.dir</name> 设置hadoop的工作目录,文件以后就保存在这里,如果文件太大,分散保存到多台机器的此目录下
<value>/var/hadoop</value> 每台机器都安装了hadoop,所以每台机器都有自己机器的工作目录,这个值每个机器可以不同。
</property>
hdfs-site.xml 文件系统的配置文件
<property>
<name>dfs.replication</name> 文件系统的副本数量
<value>2</value> 默认是3
</property>
mapred-site.xml mapreduce的配置文件
<property>
<name>mapreduce.framework.name</name> 设置mapreduce的运行框架,值有local,classic,yarn
<value>yarn</value>
</property>
yarn-site.xml yarn的配置文件
<name>yarn.resourcemanager.hostname</name> 设置resourcemanager的老大
<value>dwh</value>

<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
slaves 配置datanode节点的机器

 

------------第二种--------------
说明:
1.在hadoop2.0中通常由两个NameNode组成,一个处于active状态,另一个处于standby状态。Active NameNode对外提供服务,而Standby NameNode则不对外提供服务,仅同步active namenode的状态,以便能够在它失败时快速进行切换。
hadoop2.0官方提供了两种HDFS HA的解决方案,一种是NFS,另一种是QJM。这里我们使用简单的QJM。在该方案中,主备NameNode之间通过一组JournalNode同步元数据信息,一条数据只要成功写入多数JournalNode即认为写入成功。通常配置奇数个JournalNode
这里还配置了一个zookeeper集群,用于ZKFC(DFSZKFailoverController)故障转移,当Active NameNode挂掉了,会自动切换Standby NameNode为active状态
2.hadoop-2.2.0中依然存在一个问题,就是ResourceManager只有一个,存在单点故障,hadoop-2.6.4解决了这个问题,有两个ResourceManager,一个是Active,一个是Standby,状态由zookeeper进行协调

core-site.xml
<configuration>
<!-- 指定hdfs的nameservice为ns1 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://bi/</value>
</property>
<!-- 指定hadoop临时目录 -->
<property>
<name>hadoop.tmp.dir</name>
<value>/home/hadoop/app/hdpdata/</value>
</property>

<!-- 指定zookeeper地址 -->
<property>
<name>ha.zookeeper.quorum</name>
<value>mini5:2181,mini6:2181,mini7:2181</value>
</property>
</configuration>

hdfs-site.xml
<configuration>
<!--指定hdfs的nameservice为bi,需要和core-site.xml中的保持一致 -->
<property>
<name>dfs.nameservices</name>
<value>bi</value>
</property>
<!-- bi下面有两个NameNode,分别是nn1,nn2 -->
<property>
<name>dfs.ha.namenodes.bi</name>
<value>nn1,nn2</value>
</property>
<!-- nn1的RPC通信地址 -->
<property>
<name>dfs.namenode.rpc-address.bi.nn1</name>
<value>mini1:9000</value>
</property>
<!-- nn1的http通信地址 -->
<property>
<name>dfs.namenode.http-address.bi.nn1</name>
<value>mini1:50070</value>
</property>
<!-- nn2的RPC通信地址 -->
<property>
<name>dfs.namenode.rpc-address.bi.nn2</name>
<value>mini2:9000</value>
</property>
<!-- nn2的http通信地址 -->
<property>
<name>dfs.namenode.http-address.bi.nn2</name>
<value>mini2:50070</value>
</property>
<!-- 指定NameNode的edits元数据在JournalNode上的存放位置 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://mini5:8485;mini6:8485;mini7:8485/bi</value>
</property>
<!-- 指定JournalNode在本地磁盘存放数据的位置 -->
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/home/hadoop/journaldata</value>
</property>
<!-- 开启NameNode失败自动切换 -->
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<!-- 配置失败自动切换实现方式 -->
<property>
<name>dfs.client.failover.proxy.provider.bi</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 配置隔离机制方法,多个机制用换行分割,即每个机制暂用一行-->
<property>
<name>dfs.ha.fencing.methods</name>
<value>
sshfence
shell(/bin/true)
</value>
</property>
<!-- 使用sshfence隔离机制时需要ssh免登陆 -->
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/home/hadoop/.ssh/id_rsa</value>
</property>
<!-- 配置sshfence隔离机制超时时间 -->
<property>
<name>dfs.ha.fencing.ssh.connect-timeout</name>
<value>30000</value>
</property>
</configuration>

mapred-site.xml
<configuration>
<!-- 指定mr框架为yarn方式 -->
<property>
<name>mapreduce.framework.name</name>
<value>yarn</value>
</property>
</configuration>

yarn-site.xml
<configuration>
<!-- 开启RM高可用 -->
<property>
<name>yarn.resourcemanager.ha.enabled</name>
<value>true</value>
</property>
<!-- 指定RM的cluster id -->
<property>
<name>yarn.resourcemanager.cluster-id</name>
<value>yrc</value>
</property>
<!-- 指定RM的名字 -->
<property>
<name>yarn.resourcemanager.ha.rm-ids</name>
<value>rm1,rm2</value>
</property>
<!-- 分别指定RM的地址 -->
<property>
<name>yarn.resourcemanager.hostname.rm1</name>
<value>mini3</value>
</property>
<property>
<name>yarn.resourcemanager.hostname.rm2</name>
<value>mini4</value>
</property>
<!-- 指定zk集群地址 -->
<property>
<name>yarn.resourcemanager.zk-address</name>
<value>mini5:2181,mini6:2181,mini7:2181</value>
</property>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
</configuration>

slaves同上


###注意:严格按照下面的步骤!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1、首先启动zookeeper。
2、启动journalnode。
sbin/hadoop-daemon.sh start journalnode
#运行jps命令检验,hadoop05、hadoop06、hadoop07上多了JournalNode进程
3、格式化HDFS。
因为是同一个hadoop集群的主备,所以clusterid一定相同,所以不可以分别在主备两台机器都格式化。应该是格式化其中一台,然后把生成的文件拷贝到另一台。
但是手动拷贝太low了,hadoop有自己的命令可以拷贝到备用namenode,建议使用hadoop自带的:hdfs namenode -bootstrapStandby
4、格式化ZKFC(在主节点上执行一次即可)
hdfs zkfc -formatZK
5、启动HDFS(在主节点上执行)
sbin/start-dfs.sh
6、启动YARN
sbin/start-yarn.sh

到此,hadoop-2.6.4配置完毕,可以统计浏览器访问:
http://hadoop00:50070
NameNode 'hadoop01:9000' (active)
http://hadoop01:50070
NameNode 'hadoop02:9000' (standby)

------------第三种--------------

暂不提供


##############################
测试集群工作状态的一些指令 :
bin/hdfs dfsadmin -report 查看hdfs的各节点状态信息

bin/hdfs haadmin -getServiceState nn1 获取一个namenode节点的HA状态

sbin/hadoop-daemon.sh start namenode 单独启动一个namenode进程

./hadoop-daemon.sh start zkfc 单独启动一个zkfc进程

上传文件:hadoop dfs -put 本地文件 /hdfs路径

下载文件:hadoop dfs -get /hdfs路径

查看文件:hadoop dfs -ls /
hdfs fs

 

 


----------------hdfs工作原理-----------------
1、hdfs的整体工作原理:
读:
写:
2、hdfs中的namenode的工作原理
3、hdfs中的secondaryNamenode工作原理
4、hdfs中datanode的原理

 

--------------mapreduce的工作原理-----------
mapreduce的详细过程:
1、hadoop jar ***.jar com.itcast.WordCount 输入目录 输出目录
2、进过一系列步骤(即yarn的资源分配,有了资源才能够正真执行你写的代码,资源分配的过程见下面yarn的执行过程),启动MRAppMaster,此时用户写的代码才开始执行
3、根据job描述对文件切片,一个文件一个切片,如果一个文件过大,则会切成多片(计算逻辑:默认是block大小:128M,但是配置了参数就不一定了,Math.max(minSize, Math.min(maxSize, blockSize))),当一个文件切到最后剩下的不大于blocksize*1.1,那么这个整个就是一个切片,不会再分了
4、启动对应的maptask任务,一个切片一个maptask
5、maptask启动:
5.1:FileInputFormat读取RecordReader中的数据,k:当前行的开始位置在整个文件中的偏移量,v:当前行的内容
5.2:执行map()方法中的业务逻辑
5.3:使用outputCollector输出到内存环形缓冲区
5.4:环形缓冲区默认大小100M,阈值80%,大于80M启动一个后台线程开始溢出到磁盘
5.5:溢出到磁盘是map还在往缓冲区写内容,如果缓冲区满了,那么map任务会阻塞,直到溢出到磁盘完成
5.6:准备要溢出的数据会有分区过程,如果指定3各个recuder,那么这80M的内容会被分配到3各个分区文件中,分区后再进行排序,这里使用的是快速排序法,如果有combinner程序,那么此时还会执行初步的合并工作,所以到磁盘的文件不是很大(80M分成3分,且还有合并,大概也就20M左右) 溢出:先分区,后分区内排序,后执行combinner,后输出。
5.7:maptask执行完,溢出了很多的小文件,每10个小文件进行一次合并和分组排序,把大量的各个分区的小文件合并和排序(因为在各个小文件中已经是有序,现在的排序是归并排序),最终结果:一个文件,n个分区,且每个分区是有序的
5.8:通常会对map的输出进行压缩,压缩后往磁盘写入更快,占用更小的磁盘空间,且往reducer上传,减少数据传输量
6、至此,maptask任务结束
7、MRAppMaster跟踪nodemanagger上的任务已经结束,再次向resourceManager申请分配资源执行reducerTask,并告知reducerTask去哪儿拉取mapTask的数据
8、reducerTask启动: 通常maptask的结束时间不一致,只要有一个maptask完成,那么就启动reducer并拉取这个map的输出。
8.1:去所有的maptask上拉取属于自己的那个partition的数据,
8.2:进入合并阶段,如果有50个map输出,合并因子是10,合并执行5次,每次将10个文件合并成一个文件,最后由5个中间文件
8.3: 相同key的数据,作为一组,调用reduce()方法,执行业务逻辑
8.4:outputFormat输出结果


其中:5.3~8.1 称为shuffle
分区默认是hash分区
8.3这一步,可以有GroupingComparator,可以自定义什么样的数据为一组。

----------------yarn工作原理------------------

yarn:只是作为一个资源调度器,例如把运算的jar包移动到有数据的机器上,移动计算而不是移动数据。
1、yarn并不清楚用户提交的程序的运行机制
2、yarn只提供运算资源的调度(用户程序向yarn申请资源,yarn就负责分配资源)
3、yarn中的主管角色叫ResourceManager
4、yarn中具体提供运算资源的角色叫NodeManager
5、这样一来,yarn其实就与运行的用户程序完全解耦,就意味着yarn上可以运行各种类型的分布式运算程序(mapreduce只是其中的一种),比如mapreduce、storm程序,spark程序,tez ……
6、所以,spark、storm等运算框架都可以整合在yarn上运行,只要他们各自的框架中有符合yarn规范的资源请求机制即可
Yarn就成为一个通用的资源调度平台,从此,企业中以前存在的各种运算集群都可以整合在一个物理集群上,提高资源利用率,方便数据共享

yarn的调度过程:
1、提交任务(可能是mr任务,也可能是storm任务,也可能是spark任务)
2、要想能使用yarn来做资源调度,那么只要符合yarn规范的资源请求机制,最终都会走到yarn的入口:yarnRunner
3、yarnRunner向resourcemanager申请运行一个任务(同样可能是mr,spark,storm)
4、返回一个任务id,即jobid,以及提交资源用的stagingdir路径,一般都在hdfs上
5、提交任务的相关资源文件,例如这个任务的jar包,这个任务的配置文件,如果是hadoop任务还有切片信息文件
6、通知resourcemanager,任务的资源文件提交完毕,提交到了resourcemanager在第4步上的那个路径,
7、ressourcemanaer知道资源提交完成,经过一番调度后在nodemanager上启动MRAppMaster进程,
8、MRAppMaster启动,初始化化数据,并准备监控task的进度,然后解析任务,查看在第4步上的资源,得知需要启动几个maptask,向resourcemanager发出请求分配资源。
9、resourcemanager分配好固定的资源,通知nodemanager领取任务,讲第4步中路径下的资源下载到本地,启动yarnchild进程,真正的计算是在nodemanager上的yarnchild进程中的,
10、在nodemanager上下载任务的资源文件,并创建一个资源管理容器,以管理:cpu+ram+job资源文件。在hadoop中叫做MRAppMaster。
11、由MRAppMaster向resourcemanager申请资源运行真正的任务
12、在某台nodemanager上启动一个yarnChild,在这个里面就是真正运行任务的,例如:hadoop在这里运行maptask任务
13、向yarnChild发送运行任务的脚本,java -cp ***.jar com.itcast.wordcount ,此时才真正运行自己写的代码
14、任务执行完毕(如果是hadoop,那么此时还没有完毕,应为还要再次由MRAppMaster向resourcemanager申请,运行reduceTask)
15、由MRAppMaster向resourcemanager发送任务执行完毕的消息,注销自己


-------------------针对以上map端过程进行调优-------------
io.sort.mb 默认值100M 调整map的环形缓冲区大小
io.sort.spill.percent 0.8 环形缓冲区溢出到磁盘的阈值
io.sort.factor 10 reducer端将map的输出进行合并的流数,此值增加到100也是很常见的
mapred.compress.map.output false 未启用map输出的压缩
mapred.map.output.compression.codec map输出时的压缩方式

-------------------针对以上reducer端过程进行调优-------------
mapred.reduce.parallel.copies 5 用于把map输出赋值到reducer的线程数,增加可以提高性能
mapred.iob.reduce.input.buffer.percent 0.0 在reducer阶段,在内存中保存map输出的空间占整个对空间的比例。增加这个值,reducer就不必一次次地区磁盘查找数据,而是一下子把很大一部分数据就加载到内存了。


--------------------任务失败情况----------
任务本身失败:代码中运行出错,或异常退出,任务被标记为失败,失败后会有重试次数,4次失败,任务宣告失败。
MRAppmaster失败;因为MRAppmaster需要定时向resourcemanager发送心跳,发生故障,resourcemanager会得知并启动另一个MRAppMaster进程(有可能还在当前这个nodemanager上,因为只是MRAppMaster进程失败,机器节点是正常的),但是重启后就需要重新运行整个任务,如果设置yarn.app.mapreduce.am.job.recover.enable = true,那么重新启动该的MRAppMaster会得到失败之前的状态并接着执行。
nodemanager失败:每10分钟向resourcemanager发送给心跳信息,发生故障,如果本机上运行着MRAppMaster进程,那么在别的nodemanager上启动另一个即可,如果本机上只运行着yarnchild,那么同理,重找一台机器,重启启动即可。
resourcemanager失败:是一种非常严重的异常,作业和任务容器都无法启动,只能由管理员启动一个新的才行。resourcemanager会通过检查点机制将其状态持久性存储,从而实现从失败中会回复。

-----------------作业的调度--------
以前是来一个作业,执行一个作业,先进先出的规则
后来可以给作业设置优先级,VERY_HHIGH, HIGH, NORMAL, LOW, VERY_LOW


-----------------多个maptask或reducertask并行运行中某些“拖后腿”的任务-------------
出现原因:硬件老化或者配置错误,检测具体的原因很难,因为即使任务时间长,但任务总能够完成
hadoop并不处理"拖后腿"的任务,因为也无法处理,它只能尽量推测。
hadoop推测任务慢时,会启动另一个相同的任务作为备份,这就是所谓的"推测执行"。
只有在job的所有任务全部启动,并运行过了一段时间后,某个任务的进度比别的任务慢,才会启动"推测执行"任务。
然后就看谁先执行完毕,使用执行完毕的那个任务的数据,未执行完的则会被终止。
"推测执行"是一种优化措施,并不能使作业运行更可靠。
默认"推测执行"是启用的。可以单独为map任务或者reduce任务启用或禁用该功能。
mapred.map.tasks.speculative.execution = true(默认就是true)
但是大多数的集群管理员都会关闭该属性,而是让用户根据自己的个别作业设置。尤其是在reduce阶段,重复启动任务,会传输大量的map输出数据,增加网络压力。


----------------某些坏记录----------
以上调优或者设置都正常的情况下,程序应该会正常执行。
但是,坏记录总是存在的,例如:因为一条缺少字段的记录而导致切割后数组越界等异常,
对坏记录的处理也是必须的,
方法1:使用try catch 捕获到这个错误,continue,继续下次循环,这是直接忽略坏记录
方法2:使用try catch 捕获到错误,进行额外处理


--------------mr任务中需要自己实现的代码--------------
MyInputFormat inplements InputFormat{ //
InputSplit[] getSplits(JobConf job, int numSplits) throws IOException;
RecordReader<K, V> getRecordReader(InputSplit split, JobConf job, Reporter reporter) throws IOException;
}
1、3个重要接口:
InputFormat,定义了如何获取切片数组,以及获取一个读取器 InputSplit[] getSplits(JobConf job, int numSplits和 RecordReader<K, V> getRecordReader(InputSplit split, JobConf job, Reporter reporter)
InputSplit,定义了切片的信息:长度和地址 long getLength() 和 String[] getLocations(),注意:切片信息是需要序列化到文件的,所以它也继承了Writable接口
RecordReader, 定义了如何读取切片 ,如下:
  boolean next(K key, V value) // Reads the next key/value pair from the input for processing.
  K createKey(); // Create an object of the appropriate type to be used as a key.
  V createValue(); // Create an object of the appropriate type to be used as a value.
  long getPos(); // Returns the current position in the input.
  public void close(); // Close this {@link InputSplit} to future operations.
  float getProgress(); // How much of the input has the {@link RecordReader} consumed i.e. has been processed by?
2、以上3个接口作为一套读取特定个数据的组合,如果定义自己的InputFormat,那么需要实现上面的3个接口,例如:
FileInputFormat --> FileSplit --> LineRecordReader 读取一行一行的内容
DBInputFormat --> DBInputSplit --> DBRecordReader 读取方式需要再解析源代码
3、同上,实现自己的InputFormat,就是定义自己的切片方式,定义自己的数据的读取方式。
4、但是,也不一定需要完全实现上面的3个接口,例如:自己的一个文件,我想让他每3行读取一次,那么切片就不需要实现了,用已有的就很好,只需要实现读取器即可,而且读取器也可以对已有的读取器封装,以前的读取器每次一行,现在只需要包装一下,即调用3次原先的每行读取,然后3行一次性返回即可
5、hadoop已经帮我们实现了大部分常见的数据输入方式(文本,数据库,hiveinput,很多小文本....(具体见InputFormat的实现类)),只需要偶尔做一些小小改动即可

  
6、最常见的就是文本输入了,即TextInputFormat,现在对他进行一些代码解析:
切片方式:文件小于blocksize,那么一个文件就是一个maptask
文件大于blocksize,每128M一个切片,最后一个切片一般都是不满128M,如果小于128*0.1=12.8M,那么就每必要再启动一个maptask了,跟上一个合并即可,如果大于12.8M,那么还得启动一个maptask任务
读取方式:直接见代码如下:
public LongWritable createKey() {
return new LongWritable(); 文本的key就是一个偏移量
}

public Text createValue() {
return new Text(); 文本的value就是当前的行内容给
}
private long getFilePosition() throws IOException { 获取当前文本的读取偏移量,看文件读取到哪儿了
long retVal;
if (isCompressedInput() && null != filePosition) {
retVal = filePosition.getPos();
} else {
retVal = pos;
}
return retVal;
}
/** Read a line. */ 真正的读取一行内容
public synchronized boolean next(LongWritable key, Text value)
throws IOException {

// We always read one extra line, which lies outside the upper
// split limit i.e. (end - 1)
while (getFilePosition() <= end || in.needAdditionalRecordAfterSplit()) {
key.set(pos);

int newSize = 0;
if (pos == 0) {
newSize = skipUtfByteOrderMark(value);
} else {
newSize = in.readLine(value, maxLineLength, maxBytesToConsume(pos));
pos += newSize;
}

if (newSize == 0) {
return false;
}
if (newSize < maxLineLength) {
return true;
}

// line too long. try again
LOG.info("Skipped line of size " + newSize + " at pos " + (pos - newSize));
}

return false;
}
##########分割线#############

MyMapper implements Mapper 略,业务代码,针对输入的数据进行处理
MyPartition implements Partitioner 实现一个返回分区号的方法,以便让map知道当前的key应该被分配到哪个分区
MyCombinner implements Reducer 略,一般情况下和MyReducer一样就行,实际上都使用MyReducer来充当Combinner
MyGroupingComparator implements RawComparator 就是一个比较器而已,用来在reducer端的时候进行排序,告诉reducer它怎么给输入的数据排序而已,通常用来给自定义的key排序,
MyKeyObject implements WritableComparator 在Mapper类的map方法中,key是LongWritable类型,其实可以自定义类型,可以是自己定义的一个业务上的逻辑对象,那么自己写的这个类必须能够被序列化,必须能够被互相比较。
MyReducer implements Reducer 略, 业务代码

###########分割线#########
MyOutPutFormat implements OutPutFormat 类比于输入,也可以自己定义输出格式,但是这里就不需要定义切片信息了。只需要告诉hadoop怎么输出就行了。
MyRecordWriter implements RecordWriter 输出也有很多的实现了,输出到(文本,数据库.... 具体见OutPutFormat的实现类)
自定义输入的情况都少见(因为hadoop把能想到的输入都已经有了具体实现),自定义输出就更少见了。一般输出文档就行

-------------面试可能的问题--------------
1、计数器:
内置计数器:就是mr任务执行完毕后的统计信息,内部统计了很多信息,只打印出了一些概要信息
自定义计数器:
2、排序:部分排序,全排序,辅助排序(即二次排序)
部分排序:30个reducer,30个输出文件,每个文件中都是有序的。
全排序:方法1:使用一个reducer,但是机器压力大
方法2:创建一系列排序好的文件,然后串联这些文件,最后生成一个有序文件。主要思路是使用partitioner
但是如果不了解数据的分布,就有可能自己分配数据不均匀,产生数据倾斜,
使用采样的方式,进行数据的一个简要分析,以便用户能够对分区进行一个较为均匀的分配
采样的核心思想:查看一小部分键,获取近似分布,并由此构建分区
hadoop已经内置了多种采样器,不需要用户自己编写,
一个InputSampler接口,多个实现类,常用的是RandomSampler类,用法如下:
InputSampler<IntWritable, Text> sampler = new InputSampler.RandomSampler<IntWritable, Text>(0.1, 10000, 10); // 采样率0.1,最大样本数是10000, 最大分区数是10。当然得告诉这个采样器,你准备要分几个区。限制采样数量可以减少采样时间占总运行时间的很小一部分。
InputSampler.writePartitionerFile(job, sampler); // 调用静态方法,让他写一个分区文件
job.setPartitionerClass(TotalOrderPartitioner.class); // 必须使用全排序的partitionner,它会自动解析分区文件,如果使用默认的hashpartitioner,那么全排序就没有意义的,它就是把你自己给他指定分区改为由他来解析分区,所以必须使用这个类。
到此为止,可以均匀的分区,做到了全排序。
还有各种各样的采样器,例如:SplitSampler:采前n条记录作为样本,IntervalSampler:以一定间隔来采样。用到的时候再学习。
二次排序:针对value的排序,
将key和value合起来构成一个对象,然后将这个对象相同的key分到一个partitioner,然后写一个MyGroupingComparator implements WriableComparator,让reducer接受数据是相同的key能够到分到一组中。
3、map端join,reduce端join
map端join:适合处理一大一小文件,小文件可以缓存到本地节点。效率较高
1、在map的生命周期方法setup中,把小文件全部加载到内存
2、只读一个大文件,大文件的每行取join内存中的小文件,输出即可

reduce端join:适合处理两个大文件,reduce端连接较为常用,但是由于shuffle,效率更为低下一些
1、构建一个bean,有很多属性,这个bean是最后合并后的bean,包含了两张表的全部字段,然后再加一个额外字段,用以表示当前的属性是哪张表的
2、map端读取文件,在maptask中根据文件名获取当前读取的是哪个文件的block块,然后将一行内容拆分,存入bean,并标识现在是哪个表的属性,
3、map的输出内容:key是join时的on的条件,value是bean
4、reducer收到on条件的所有数据,有好多的bean,且每个bean现在只有一半的属性,且知道这些属性是从哪个表来的。
5、处理这写bean,左表得到唯一一个bean即可,右表得到多个bean
6、将bean进行整合成一个大bean,输出即可。
上面两个是自己实现连接:其实hadoop的InputFormat已经实现了map端连接,使用CompositeInputFormat,然后输入参数,即可连接
CompositeInputFormat --> CompositeInputSplit --> ComposableRecordReader 具体用法未详细了解 可以设置输入源,连接类型(内连接或者外链接)。 org.apache.hadoop.examples.join 是一个通用的执行map端连接的命令行程序样例,可作为参考。

4、分布式缓存:略

5、数据倾斜
原因:数据分布不均匀,28原则,%20的人掌握了80%的财富
目标:把这20%拆分,使之均匀分布
具体实施方法:
1、增加reducer数量
2、自定义partitioner
3、使用采样,随机采样,分区
4、join时和groupby时分区

6、如何在20亿个数据中取得前100个最大值?

7、大量小文件如何处理?
共3中方式:
1、提前预防:在存入hdfs时就合并完在put到hdfs上
2、执行业务前:如果已经有大量的小文件在hdfs上了,那么可以用一次mr程序合并一次,这次跟业务无关,只是合并而已
程序的核心机制:
自定义一个InputFormat
改写RecordReader,实现一次读取一个完整文件封装为KV
在输出时使用SequenceFileOutPutFormat输出合并文件
3、任务执行中:可采用combineInputFormat提高效率

---------------集群的配置与管理--------------

 

---------------上面有mr程序过程的调优,以下是hadoop集群本身的调优------------


---------------mr程序的源码解析------------

 

posted @ 2017-09-04 17:11  IT豪哥  阅读(258)  评论(0编辑  收藏  举报