MapeReduce编程规范、工作机制 ||FileInputFormat切片 || Shuffle || Partition ||WritableComparable排序 || Combiner

MapReduce编程规范

Mapper

  • Mapper继承父类
  • 输入数据是KV对
  • 业务逻辑写在map方法中
  • 输出数据是KV对
  • map()方法(MapTask进程)对每一个<K,V>调用一次

Reducer

  • Reducer继承父类
  • 输入数据是KV
  • 业务逻辑写在reduce方法中)
  • ReduceTask进程对每一组相同k的<k,v>组调用一次reduce()方法

Driver

  • 用于提交整个程序到YARN集群,提交的是
    封装了MapReduce程序相关运行参数的job对象

MapTask工作机制

五个阶段

job 的提交流程

  • 客户端对任务切片划分,提交给yarn,提交内容有:切片、jar包、job.xml,yarn开启MrAppMaster计算出MapTask数量

  • Read
    由InputFormat(默认TextInputformat)中的RecorderReader读取数据(k,v)

  • Map
    进入到map()方法中,输出(k,v)

  • Collect、溢写阶段、Merge阶段
    向环形缓冲区中写入(k,v), 一侧索引,一侧数据(默认大小100M),到达80%后反向溢写。
    溢写的时候会进行分区,并默认按照key的hashcode值,对reduceTask进行取余。根据余数相同的分到一个分区中。
    在分区时还会进行排序,默认按字典顺序。使用快排(利用元数据Keystart、valstart进行排序)。
    Key -> key的hashcode ->根据reduceTask的个数取余->根据取余的结果进行分区。
    在MapTask结束的时候,会将相同分区的数据(来自多个溢写文件)聚合到一块(保证每一个分区内部有序)。并进行排序,使用归并排序。MapTask自此结束。
    在这里插入图片描述

ReduceTask工作机制

三个阶段
MapTask数据准备好,已经持久化在磁盘上,等待Reduce拉取
Copy
(来自多个MapTask任务)拉取指定分区的数据
Sort
(来自多个MapTask任务)相同分区的拉取到一块,进行合并和排序,归并排序。一个ReduceTask去处理一个分区的数据。
Reduce
ReduceTask会根据相同的key分组,key相同的数据被分为了一组。一组数据去调用一次reduce方法。一个reduceTask处理完以后使用OutputFormat写入到一个reduceTask文件中。

按照 MapReduce 语义,用户编写 reduce()函数输入数据是按 key 进行聚集的一组数据。为了将 key 相同的数据聚在一起,Hadoop 采用了基于排序的策略。各个 MapTask 已经实现对自己的处理结果进行了局部排序,因此,ReduceTask 只需对所有数据进行一次归并排序即可。

在这里插入图片描述

InputFormat数据输入

FileInputFormat

FileInputFormat 常见的接口实现类包括:TextInputFormat、KeyValueTextInputFormat、
NLineInputFormat、CombineTextInputFormat 和自定义 InputFormat 等

  • Math.max(minSize, Math.min(maxSize, blockSize));

-每次切片前都要判断剩下的部分是否大于块的1.1倍,不大于1.1倍就划分一块切片

比如文件大小260M,每一个块大小分为128M。过程:(260/128=2.03>1.1?) 此时切128M 剩下132M(132/128=1.03>1.1?)此时不大于 所以 将132M 放在一个切片。也就是实际是将260M 分成了两个Spilt 一个是128M 一个是132M

  • 将切片信息写到一个切片规划文件中
  • inputsplit只记录了切片的元数据信息
  • 提交切片规划文件到YRAN上,其MrAppMaster可以根据切片规划文件开启MapTask个数
  • (提交xml文件)

TextInputFormat

是默认的 FileInputFormat 实现类。
按行读取每条记录。
键是偏移量(该行在整个文件中的起始字节)LongWritable类型。
值是这行的内容,不包括行结束符,Text类型。

CombineTextInputFormat

用于小文件过多的场景
可以将多个小文件从逻辑上规划到一个切片中。
在这里插入图片描述

Partition Comparable

  • MapTask收集map()方法中输出的kv对,放到内存缓冲区中(圆形内存缓冲区,分为索引(元数据)和数据两个分区)。
  • 当环形缓冲区的使用率达到一定阈值后,再对缓冲区中的数据进行一次快排溢写到磁盘,数据处理完毕后,磁盘上的所有文件进行归并排序。
  • 多个溢出文件会合并(过程调用Partitioner进行分区,并对key排序)
  • ReduceTask根据分区号在MapTask上拉取相应的分区数据,若文件大小超过一定阈值,溢写在磁盘上,否则存储在内存。若磁盘上的文件大小超过阈值,进行归并生成更大文件。当所有数据拷贝完毕后,统一对内存和磁盘上的所有数据进行一次归并排序。
  • ReduceTask合并文件(同一分区不同MapTask的结果文件)
  • shuffle过程结束

Shuffle

Map后Reduce前的数据处理过程

自定义Partition分区

  • 自定义类继承partitioner,重写getpartition()方法
  • job驱动中设定.class
  • 设置相应数量的reducetask job.setReduceTask(x);
    在这里插入图片描述

WritableComparable 排序

在这里插入图片描述
自定义排序bean对象作为key传输,需要实现WritableComparable接口重写compareTo方法。

Combiner合并

在这里插入图片描述

自定义combiner
继承reducer,重写reduce方法
job中设置setCombinerClass
在这里插入图片描述
对每一个MapTask的输出局部汇总
在这里插入图片描述

OutputFormat 数据输出

常见的OutputFormat实现类
在这里插入图片描述

  • 默认输出格式TextOutputFormat
  • 自定义OutputFormat
    输出到mysql/hbase…存储框架中
    自定义一个类继承FileOutputFormat。
    ➢ 改写RecordWriter,具体改写输出数据的方法write()。
    在这里插入图片描述

切片 MapTask并行度

  • 数据块:物理上的分块
  • 数据切片:逻辑上对输入进行分片,并不会在磁盘上将其分成片进行存储。一个切片对应一个maptask
  • 默认情况下,切片大小=块大小
  • MapTask 并行度由切片个数决定,切片个数由输入文件和切片规则决定。
    在这里插入图片描述

ReduceJoin

缺点:这种方式中,合并的操作是在 Reduce 阶段完成,Reduce 端的处理压力太大,Map节点的运算负载则很低,资源利用率不高,且在 Reduce 阶段极易产生数据倾斜。
解决方案:Map 端实现数据合并

MapJoin

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Spark SQL中的小文件问题,可以通过合并小文件来解决。以下是OPPO的解决方案,包括具体的实现细节和代码。 1. 使用Hadoop的FileInputFormat和TextInputFormat读取小文件并转换为RDD: ``` val input = sc.newAPIHadoopFile(inputPath, classOf[TextInputFormat], classOf[LongWritable], classOf[Text], sc.hadoopConfiguration) val rdd = input.map(pair => pair._2.toString) ``` 2. 将RDD转换为DataFrame: ``` val schema = StructType(Seq(StructField("line", StringType))) val rowRDD = rdd.map(Row(_)) val df = spark.createDataFrame(rowRDD, schema) ``` 3. 将DataFrame写入临时目录,并设置合并小文件的参数: ``` df.write .option("mergeSchema", "true") .option("maxRecordsPerFile", "1000000") .parquet(tempOutputPath) ``` 其中,"mergeSchema"表示是否合并不同文件的schema,"maxRecordsPerFile"表示每个文件包含的最大记录数。 4. 使用Hadoop的FileUtil将临时目录中的小文件合并为一个大文件: ``` val fs = FileSystem.get(sc.hadoopConfiguration) FileUtil.copyMerge(fs, new Path(tempOutputPath), fs, new Path(outputPath), false, sc.hadoopConfiguration, null) ``` 其中,tempOutputPath为临时目录,outputPath为最终输出目录。 完整的代码如下: ``` import org.apache.hadoop.fs.{FileSystem, Path} import org.apache.hadoop.io.{LongWritable, Text} import org.apache.hadoop.mapreduce.lib.input.TextInputFormat import org.apache.spark.sql.{Row, SparkSession} import org.apache.spark.sql.types.{StringType, StructField, StructType} object MergeSmallFiles { def main(args: Array[String]): Unit = { val inputPath = args(0) val outputPath = args(1) val tempOutputPath = outputPath + "/temp" val spark = SparkSession.builder().appName("MergeSmallFiles").getOrCreate() val sc = spark.sparkContext val input = sc.newAPIHadoopFile(inputPath, classOf[TextInputFormat], classOf[LongWritable], classOf[Text], sc.hadoopConfiguration) val rdd = input.map(pair => pair._2.toString) val schema = StructType(Seq(StructField("line", StringType))) val rowRDD = rdd.map(Row(_)) val df = spark.createDataFrame(rowRDD, schema) df.write .option("mergeSchema", "true") .option("maxRecordsPerFile", "1000000") .parquet(tempOutputPath) val fs = FileSystem.get(sc.hadoopConfiguration) FileUtil.copyMerge(fs, new Path(tempOutputPath), fs, new Path(outputPath), false, sc.hadoopConfiguration, null) spark.stop() } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值