Hadoop3教程(十三):MapReduce中的分区

文章目录

(96) 默认HashPartitioner分区

分区,是Shuffle里核心的一环,不同分区的数据最终会被送进不同的ReduceTask去处理。之前的几个小节里也都讲过分区。

Hadoop里默认的分区方式是HashPartitioner分区,核心代码:

java 复制代码
public class HashPartitioner<K, V> extends Partitioner<K, V> {
	public int getPartition(K key, V value, iint numReduceTasks) {
		return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
    }
}

在HashPartitioner里,每个key分到哪个ReduceTask(可以理解成Key属于哪个分区),是根据每个key的hashCode对ReduceTask的个数取模得到的,用户是没法控制的。

这里是为什么还要& Integer.MAX_VALUE呢?

主要是为了防止溢写,通过& Integer.MAX_VALUE,将key的hash值控制在Integer.MAX_VALUE及之下。

从代码里看,在往环形缓冲区写的时候,如果识别到numReduceTasks > 1,则启用HashPartitioner分区,如果numReduceTasks = 1,那就不启用了,直接return numReduceTasks - 1

我们也可以自定义Partitioner,自定义类需要继承Partitioner类,并重写里面的getPartition()方法。

java 复制代码
public class CustomPartitioner extendsPartitioner<Text, FlowBean>{
    @override
    public int getPartition(Text key, FlowBean value, int numPartitions){
        //控制分区代码逻辑
        。。。。。。
    	return partition;
    }
    
}

然后在驱动类里,设置上写好的自定义Partitioner:

java 复制代码
job.setPartitionerClass(CustomPartitioner.class);

最后再设置上ReduceTask的数量:

java 复制代码
job.setNumReduceTasks(5);

如果不设置ReduceTask的数量,那分区数默认是1,直接return 0,不会启用自定义分区。

(97) 自定义分区案例

首先抛出一个需求:将一堆手机号按照归属地的省份输出到不同的文件里。

已有一个phone_data.txt文件。

所以期望的输出数据是什么样子的呢?

手机号136/137/138/139开头的分别放进4个独立的文件里,然后其他的手机号放到一个文件里。最终形成5个文件。

显而易见,这个需求的核心在于自定义分区上。

所以我们需要写一个自定义分区类,假设它叫ProvincePartitioner,我们希望它能做到以下分配:

复制代码
136 分区0
137 分区1
138 分区2
139 分区3
其他 分区4

等分区类建好后,别忘记在驱动里注册上这个类,并定义好ReduceTask数量。

java 复制代码
job.setPartitionerClass(ProvincePartitioner.class);
job.setNumReduceTasks(5);

展示一下ProvincePartitioner类的代码:

java 复制代码
package com.atguigu.mapreduce.partitioner;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;

public class ProvincePartitioner extends Partitioner<Text, FlowBean> {

    @Override
    public int getPartition(Text text, FlowBean flowBean, int numPartitions) {
        //获取手机号前三位prePhone
        String phone = text.toString();
        String prePhone = phone.substring(0, 3);

        //定义一个分区号变量partition,根据prePhone设置分区号
        int partition;

        if("136".equals(prePhone)){
            partition = 0;
        }else if("137".equals(prePhone)){
            partition = 1;
        }else if("138".equals(prePhone)){
            partition = 2;
        }else if("139".equals(prePhone)){
            partition = 3;
        }else {
            partition = 4;
        }

        //最后返回分区号partition
        return partition;
    }
}

(98)分区数与Reduce个数的总结

思考这么一个问题,如果自定义Partitioner中定义了5个分区,但是驱动类里注册的时候,只声明了4个分区,即job.setNumReduceTask=4,那这时候代码会正常运行么?

不会,会报java.io.IOException。

至于为什么报IO异常,自然是MapTask中,在往环形缓冲器Collector里写的时候,发现没有第5个分区,写不进去当然就报IO异常。

但是,设置job.setNumReduceTask=1,代码是可以跑的,这是为什么呢?

原因其实之前提过,这是因为设置为1后,MapTask里,Collector在collect数据的时候,分区就不走我们自定义的Partitioner,而是直接return 0了,到最后Reduce阶段也只会生成一个文件。

这里是有点反直觉的,需要注意。

那我如果job.setNumReduceTask=6呢,代码还能跑吗?

可以跑,且会生成6个文件,只不过第6个文件是空的。

总结一下:

  • 当NumReduceTask > getPartition()里定义的分区数量,可以正常运行,但是相应的,会多余生成一些空的文件,浪费计算资源和存储资源;
  • 当 1 < NumReduceTask < getPartition()分区量,会报IO异常,因为少的那一部分分区的数据会无法写入;
  • 当NumReduceTask = 1时,不会调用自定义分区器,而是会将所有的数据都交付给一个ReduceTask,最后也只会生成一个文件。
  • 自定义分区类时,分区号必须从0开始,且必须是连续的,即是逐一累加的

最后一条比较重要,即必须是0/1/2/3/4/5/...这种形式,而不能是0/10/11/20这种。

2023-7-24 17:08:08 我有个小问题,就是驱动类里设置setNumReduceTask的时候,能不能设置成动态的,就是根据输入数据调整的呢?

查了一下,确实是有这种取巧的方式,比如说使用自定义的InputFormat,在读取数据的同时,获取数据量的情况,并根据这些信息动态调整ReduceTask的数量。这里就不多讲了,有兴趣可以查查。

参考文献

  1. 【尚硅谷大数据Hadoop教程,hadoop3.x搭建到集群调优,百万播放】
相关推荐
逸模5 小时前
告别熬夜手工整理台账,逸模智能归集实现项目数据自动化存档
大数据·运维·人工智能·笔记·其他·信息可视化·自动化
audyxiao0017 小时前
ICLR 2026论文分享 | WorldGym:用世界模型打造机器人策略评估新范式
大数据·人工智能·大模型·智能体·世界模型
Rubin智造社8 小时前
Anthropic安全白皮书2|三级成熟度模型:你的AI智能体该配哪级安全?
大数据·安全·沙箱隔离·零信任成熟度模型·三级安全框架·jit权限·不可变审计
ACP广源盛139246256738 小时前
GSV2221 显示转换芯片@ACP#赋能 RTX Spark 端侧 AI 设备,构建多屏全模态视觉交互新生态
大数据·人工智能·嵌入式硬件·gpt·spark·电脑·音视频
字节跳动开源8 小时前
你的 Agent 每次都“失忆”?这个工具彻底治好了我的前端开发焦虑
大数据·开源·agent
APItesterCris10 小时前
实战教程:借助 Open Claw + 淘宝商品 API,低成本实现电商自动化监控与智能选品
大数据·运维·自动化
团象科技10 小时前
从一线运营场景观察 海外云 独立站的跨境效能释放实践路径
大数据·人工智能
宸津-代码粉碎机10 小时前
Spring AI企业级实战|从RAG优化到Agent多工具调度
java·大数据·人工智能·后端·python·spring
INFINI Labs10 小时前
Elasticsearch 6/7/8 到 Easysearch 2.x 迁移指南
大数据·elasticsearch·mybatis·向量·snapshot
小柒儿33610 小时前
汪进进:深水区里以质立身,做长期价值的践行者
大数据·人工智能