mapreduce处理的数据是什么结构的?每个阶段有什么形式?

本文深入解析了MapReduce的工作流程,包括Map阶段的输入解析、处理及分区,以及Reduce阶段的shuffle过程、合并与排序。并通过Java代码示例,详细展示了如何使用MapReduce进行单词计数任务。

mapreduce处理的数据的结构:

map:(k1,v1)->list(k2,v2)

reduce:(k2,list(v2))->list(k3,v3)

1、Map阶段的任务处理如下:

  1.1 读取HDFS中的文件。每一行解析成一个<k,v>。每一个键值对调用一次map函数。  <0,hello you>   <10,hello me>                    

  1.2 覆盖map(),接收1.1产生的<k,v>,进行处理,转换为新的<k,v>输出。 <hello,1> <you,1> <hello,1> <me,1>

  1.3 对1.2输出的<k,v>进行分区。默认分为一个区。详见《Partitioner

  1.4 对不同分区中的数据进行排序(按照k)、分组。分组指的是相同key的value放到一个集合中。 

             排序后:<hello,1> <hello,1> <me,1> <you,1>  分组后:<hello,{1,1}><me,{1}><you,{1}>

  1.5 (可选)对分组后的数据进行归约。详见《Combiner

2、Reduce阶段的任务处理如下:

  2.1 多个map任务的输出,按照不同的分区,通过网络copy到不同的reduce节点上。(shuffle)详见《shuffle过程分析

  2.2 对多个map的输出进行合并、排序。覆盖reduce函数,接收的是分组后的数据,实现自己的业务逻辑,

          <hello,2> <me,1> <you,1>

    处理后,产生新的<k,v>输出。

  2.3 对reduce输出的<k,v>写到HDFS中。

 

Java代码实现

注:要导入org.apache.hadoop.fs.FileUtil.java。

1、先创建一个hello文件,上传到HDFS中

 

2、然后再编写代码,实现文件中的单词个数统计(代码中被注释掉的代码,是可以省略的,不省略也行)

 

package mapreduce;

import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; 
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; 

public class WordCountApp {
    static final String INPUT_PATH = "hdfs://mode1:9000/usr/hello";
    static final String OUT_PATH = "hdfs://mode1:9000/usr/out";

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        FileSystem fileSystem = FileSystem.get(new URI(INPUT_PATH), conf);
        Path outPath = new Path(OUT_PATH);
        if (fileSystem.exists(outPath)) {
            fileSystem.delete(outPath, true);
        }

        Job job = new Job(conf, WordCountApp.class.getSimpleName());

        // 1.1指定读取的文件位于哪里
        FileInputFormat.setInputPaths(job, INPUT_PATH);
        // 指定如何对输入的文件进行格式化,把输入文件每一行解析成键值对
        //job.setInputFormatClass(TextInputFormat.class);

        // 1.2指定自定义的map类
        job.setMapperClass(MyMapper.class);
        // map输出的<k,v>类型。如果<k3,v3>的类型与<k2,v2>类型一致,则可以省略
        //job.setOutputKeyClass(Text.class);
        //job.setOutputValueClass(LongWritable.class);

        // 1.3分区
        //job.setPartitionerClass(org.apache.hadoop.mapreduce.lib.partition.HashPartitioner.class);
        // 有一个reduce任务运行
        //job.setNumReduceTasks(1);

        // 1.4排序、分组

        // 1.5归约

        // 2.2指定自定义reduce类
        job.setReducerClass(MyReducer.class);
        // 指定reduce的输出类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(LongWritable.class);

        // 2.3指定写出到哪里
        FileOutputFormat.setOutputPath(job, outPath);
        // 指定输出文件的格式化类
        //job.setOutputFormatClass(TextOutputFormat.class);

        // 把job提交给jobtracker运行
        job.waitForCompletion(true);
    }

    /**
     * 
     * KEYIN     即K1     表示行的偏移量 
     * VALUEIN     即V1     表示行文本内容 
     * KEYOUT     即K2     表示行中出现的单词 
     * VALUEOUT 即V2        表示行中出现的单词的次数,固定值1
     * 
     */
    static class MyMapper extends
            Mapper<LongWritable, Text, Text, LongWritable> {
        protected void map(LongWritable k1, Text v1, Context context)
                throws java.io.IOException, InterruptedException {
            String[] splited = v1.toString().split("\t");
            for (String word : splited) {
                context.write(new Text(word), new LongWritable(1));
            }
        };
    }

    /**
     * KEYIN     即K2     表示行中出现的单词 
     * VALUEIN     即V2     表示出现的单词的次数 
     * KEYOUT     即K3     表示行中出现的不同单词
     * VALUEOUT 即V3     表示行中出现的不同单词的总次数
     */
    static class MyReducer extends
            Reducer<Text, LongWritable, Text, LongWritable> {
        protected void reduce(Text k2, java.lang.Iterable<LongWritable> v2s,
                Context ctx) throws java.io.IOException,
                InterruptedException {
            long times = 0L;
            for (LongWritable count : v2s) {
                times += count.get();
            }
            ctx.write(k2, new LongWritable(times));
        };
    }
}

3、运行成功后,可以在Linux中查看操作的结果

 

<think> 嗯,用户现在想了解Hive的Map阶段调优方法,这确实是大数据查询优化中很实际的问题。从之前的对话看,用户已经对Hive调优有一定了解,特别是数据倾斜和分区相关的内容,现在更聚焦在Map阶段的优化上。 用户可能正在处理一个实际的大数据任务,遇到了Map阶段效率低下的情况。考虑到Hive作业中Map阶段承担着数据读取、过滤和初步处理的重要作用,优化这个阶段对整体性能提升很关键。用户应该需要具体可操作的参数调整和技术手段,而不仅仅是理论说明。 我注意到引用材料里提到几个关键点:map-side聚合可以提前减少数据量,内存参数控制能避免OOM,小文件合并对Hadoop生态特别重要。用户可能还关心如何在不改代码的情况下快速生效的配置项,以及更深层的原理比如Map任务并行度控制。 需要特别注意用户上次询问过动态分区的倾斜问题,这次虽然问的是Map阶段,但可以适当关联——比如当Map输出数据分布不均时,会导致后续Shuffle和Reduce的问题。不过当前问题还是聚焦Map阶段,其他内容点到为止即可。 从专业角度,Map调优要分层次说明:首先是输入阶段(文件读取),然后是计算过程(内存/CPU),最后是输出(写磁盘)。每个层面都有对应的优化点,比如输入阶段要解决小文件,计算阶段要开map端聚合,输出阶段要考虑压缩。参数设置要给出具体值范围和生效条件,避免用户盲目套用。 用户可能还会关心如何验证优化效果,所以最后要提一下查看JobHistory的方法。考虑到用户熟悉技术,可以适当使用HiveQL示例,但要以注释说明为主,避免大段代码干扰主要观点。 </think> 以下是Hive Map阶段的系统级调优方法,结合核心参数与执行原理分层说明: --- ### 一、输入阶段优化(数据读取) 1. **小文件合并** ```sql SET hive.input.format = org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; -- 合并小文件 SET mapred.max.split.size=256000000; -- 单个Map最大处理数据量(256MB) SET mapred.min.split.size.per.node=128000000; -- 单节点最小处理量 ``` **原理**:减少Map任务数量,避免大量小文件导致的启动开销[^3] 2. **ORC/Parquet优化** 使用列式存储并启用谓词下推: ```sql SET hive.optimize.ppd=true; -- 谓词下推 SET hive.exec.orc.split.strategy=BI; -- ORC智能分片(避免读大文件时数据倾斜) ``` --- ### 二、计算阶段优化(内存与CPU) 1. **Map-Side聚合** ```sql SET hive.map.aggr=true; -- 启用Map端聚合 SET hive.map.aggr.hash.percentmemory=0.5; -- Map端Hash表最大内存占比 ``` **效果**:对`GROUP BY`提前聚合,减少Shuffle数据量50%+[^1] 2. **内存分配策略** ```sql SET mapreduce.map.memory.mb=4096; -- Map任务容器内存(根据集群调整) SET mapreduce.map.java.opts=-Xmx3072m; -- JVM堆大小(建议为0.75*容器内存) SET hive.auto.convert.join.noconditionaltask.size=3000; -- MapJoin自动转换阈值(MB) ``` 3. **向量化计算** ```sql SET hive.vectorized.execution.enabled=true; -- 启用向量化 SET hive.vectorized.execution.reduce.enabled=true; ``` **适用场景**:批量处理数据列,提升CPU利用率3-5倍 --- ### 三、输出阶段优化(数据写出) 1. **压缩中间结果** ```sql SET mapreduce.map.output.compress=true; SET mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.SnappyCodec; ``` **优势**:减少Shuffle网络传输量,Snappy压缩速度/比例均衡 2. **环形缓冲区优化** ```sql SET mapreduce.task.io.sort.mb=512; -- 排序缓冲区大小(MB) SET mapreduce.map.sort.spill.percent=0.8; -- 缓冲区溢写阈值 ``` **调优建议**:缓冲区大小设为容器内存的25%-30% --- ### 四、高级优化技巧 | 场景 | 方案 | 参数示例 | |------|------|----------| | **Map任务并行度不足** | 手动控制并行度 | `SET mapred.map.tasks=500;` | | **复杂JSON解析** | 启用JSON懒解析 | `SET hive.lazysimple.extended_boolean_literal=true;` | | **Map端Join倾斜** | 启用倾斜连接优化 | `SET hive.optimize.skewjoin=true;` | --- ### 五、调优效果验证 1. **查看执行计划** ```sql EXPLAIN FORMATTED SELECT count(*) FROM sales WHERE dt='2023-01-01'; ``` 检查是否有`Map Operator Tree`优化提示(如`aggergations:true`) 2. **监控指标对比** | 优化前 | 优化后 | 关键指标 | |--------|--------|----------| | 120个Map任务 | 35个Map任务 | `Hadoop:Map task count` | | 平均运行5min | 平均运行1.5min | `Duration` | | GC时间占比40% | GC时间占比12% | `GC Time` | > **注**:建议每次只调整1-2个参数,通过`JobHistory`对比效果[^3][^1] --- ### 附录:参数对照表 | 参数 | 默认值 | 建议值 | 作用 | |------|--------|--------|------| | `hive.map.aggr` | false | true | Map聚合开关 | | `mapred.max.split.size` | 128MB | 256MB | 分片上限 | | `mapreduce.map.memory.mb` | 1024 | 4096 | 容器内存 | | `hive.vectorized.execution.enabled` | false | true | 向量化开关 |
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值