优点
1)MapReduce易于编程
它简单的实现一些接口,就可以完成一个分布式程序,这个分布式程序可以分布到大量廉价的PC机器上运行。也就是说你写一个分布式程序,跟写一个简单的串行程序是一模一样的。就是因为这个特点使得MapReduce编程变得非常流行。
2)良好的扩展性
当你的计算资源不能得到满足的时候,你可以通过简单的增加机器来扩展它的计算能力。
3)高容错性
MapReduce设计的初衷就是使程序能够部署在廉价的PC机器上,这就要求它具有很高的容错性。比如其中一台机器挂了,它可以把上面的计算任务转移到另外一个节点上运行,不至于这个任务运行失败,而且这个过程不需要人工参与,而完全是由Hadoop内部完成的。
4)适合PB级以上海量数据的离线处理
可以实现上千台服务器集群并发工作,提供数据处理能力。
缺点
1)不擅长实时计算
MapReduce无法像MySQL一样,在毫秒或者秒级内返回结果。
2)不擅长流式计算
流式计算的输入数据是动态的,而MapReduce的输入数据集是静态的,不能动态变化。这是因为MapReduce自身的设计特点决定了数据源必须是静态的。
3)不擅长DAG(有向无环图)计算
多个应用程序存在依赖关系,后一个应用程序的输入为前一个的输出。在这种情况下,MapReduce并不是不能做,而是使用后,每个MapReduce作业的输出结果都会写入到磁盘,会造成大量的磁盘IO,导致性能非常的低下。
普通api
//获取配置信息和job对象
Configuration conf = new Configuration();
Job job = Job.getInstance(conf)
Hadoop序列化(Writable)
java序列化是一个重量级序列化框架,每个对象被序列化会附带很多额外信息(各种校验信息,Header,继承体系等),不便于在网络传输。
Hadoop序列化特点:
1.紧凑:高效使用存储空间
2.快速:读写数据额外开销少
3.互操作:支持多语言交互
使用:1实现Writable接口
2反序列化会反射空参构造方法,创建空参构造器
3重写[反]序列化方法(顺序一致)
4bean做key重写careparable
MapReduce原理
1.一个Job的Map阶段并行度由客户端在提交Job时的切片数决定
2.一个split切片分配一个MapTask并行处理
3.默认情况下,切片大小=BlockSize
4.切片时不考虑数据集整体,而是逐个针对每一个文件单独切片
MapReduce工作流程
(1)Read阶段:MapTask通过InputFormat获得的RecordReader,从输入InputSplit中解析出一个个key/value。
(2)Map阶段:该节点主要是将解析出的key/value交给用户编写map()函数处理,并产生一系列新的key/value。
(3)Collect收集阶段:在用户编写map()函数中,当数据处理完成后,一般会调用OutputCollector.collect()输出结果。在该函数内部,它会将生成的key/value分区(调用Partitioner),并写入一个环形内存缓冲区中。
(4)Spill阶段:即“溢写”,当环形缓冲区满后,MapReduce会将数据写到本地磁盘上,生成一个临时文件。需要注意的是,将数据写入本地磁盘之前,先要对数据进行一次本地排序,并在必要时对数据进行合并、压缩等操作。
溢写阶段详情:
步骤1:利用快速排序算法对缓存区内的数据进行排序,排序方式是,先按照分区编号Partition进行排序,然后按照key进行排序。这样,经过排序后,数据以分区为单位聚集在一起,且同一分区内所有数据按照key有序。
步骤2:按照分区编号由小到大依次将每个分区中的数据写入任务工作目录下的临时文件output/spillN.out(N表示当前溢写次数)中。如果用户设置了Combiner,则写入文件之前,对每个分区中的数据进行一次聚集操作。
步骤3:将分区数据的元信息写到内存索引数据结构SpillRecord中,其中每个分区的元信息包括在临时文件中的偏移量、压缩前数据大小和压缩后数据大小。如果当前内存索引大小超过1MB,则将内存索引写到文件output/spillN.out.index中。
(5)Merge阶段:当所有数据处理完成后,MapTask对所有临时文件进行一次合并,以确保最终只会生成一个数据文件。
当所有数据处理完后,MapTask会将所有临时文件合并成一个大文件,并保存到文件output/file.out中,同时生成相应的索引文件output/file.out.index。
在进行文件合并过程中,MapTask以分区为单位进行合并。对于某个分区,它将采用多轮递归合并的方式。每轮合并mapreduce.task.io.sort.factor(默认10)个文件,并将产生的文件重新加入待合并列表中,对文件排序后,重复以上过程,直到最终得到一个大文件。
让每个MapTask最终只生成一个数据文件,可避免同时打开大量文件和同时读取大量小文件产生的随机读取带来的开销。
(1)Copy阶段:ReduceTask从各个MapTask上远程拷贝一片数据,并针对某一片数据,如果其大小超过一定阈值,则写到磁盘上,否则直接放到内存中。
(2)Sort阶段:在远程拷贝数据的同时,ReduceTask启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。按照MapReduce语义,用户编写reduce()函数输入数据是按key进行聚集的一组数据。为了将key相同的数据聚在一起,Hadoop采用了基于排序的策略。由于各个MapTask已经实现对自己的处理结果进行了局部排序,因此,ReduceTask只需对所有数据进行一次归并排序即可。
(3)Reduce阶段:reduce()函数将计算结果写到HDFS上。
Shuffle机制
FileInputFormat切片机制
1.按文件内容长度切片
2.切片大小默认等于Block大小
3.切片不考虑数据集整体,逐个针对每个文件单独切片
4.获取切片信息api
(FileInput)context.getInputSplit.getPath().getName
TextInputFormat
TextInputFormat是FileInputFormat默认的实现类。逐行读取记录。键是该行在整个文件的起始偏移量,LongWritable类型。值是该行内容,不包括任何终止符,Text类型。
CombineTextInputFormat
应用于小文件过多的场景,它可以将多个小文件从逻辑上规划到一个切片中,这样,多个小文件就可以交给一个MapTask。
1.虚拟机设置最大虚拟内存
CombineTextInputFormat.setMaxInputSplitSize(job,大小[b]);
2.切片机制
1)切片过程
2maxsize>size>maxsize 切片 分为大小相同的两块
size>2maxsize 切片
2)虚拟存储过程
按字典顺序合并切片
3.代码差异
job.setInputFormatClass(CombineTextInputFormat.class);
CombinTextInput.setMaxInputSplitSize(job, 20971520);
Patition分区
1.自定义Partitioner继承Partitioner类,实现getPartition方法。
2.驱动类设置Partitioner
job.setPartitionerClass()
3.设置reduceTask数量
job.setNumReduceTask();
注:getPartition>reduceTask 会生成空的输出文件;getPartition<reduceTask会抛出异常;reduceTask=1,只有一个输出文件
Combiner合并
1.Combiner是M和R之外的一种组件
2.Combiner的父类是Reducer
3.Combiner运行在每个MapTask节点。Reducer是接收全局所有Mapper的输出结果
4.Combiner的意义是对每个MapTask的输出进行局部汇总,减少网络传输量
5.使用Combiner的前提是不能影响最终业务逻辑
6.代码差异
job.setCombinerClass();
OutputFormat
重写OutputFormat
重写RecordWriter
ReduceJoin
map端打标签,reduce端接收合并。
用(FileSplit)context.getInputSplit()获取文件信息,进行分类打标签就行了
MapJoin
适合一张表十分小,一张表很大的场景。
Reduce端处理过多表,容易发生数据倾斜。在Map端缓存表,提前处理业务逻辑,增加map端业务,减少reduce任务,尽可能减少数据倾斜。
代码差异:
放入缓存的文件
job.addCacheFile(new URI("hdfs://.../xx.log"))
setup方法里:
//通过缓存文件得到小表数据pd.txt
URI[] cacheFiles = context.getCacheFiles();
Path path = new Path(cacheFiles[0]);
//获取文件系统对象,并开流
FileSystem fs = FileSystem.get(context.getConfiguration());
FSDataInputStream fis = fs.open(path);
//通过包装流转换为reader,方便按行读取
BufferedReader reader = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
ETL(数据清洗)
描述数据从来源端经过抽取,转换,加载至目的端的过程 。