hadoop 概要

其实做了这么些大数据的东西,包括BI工具做可视化,我们至今是没有引入hadoop的,纯硬解硬写,我在此文建立的时刻还是没有真实的项目内使用hadoop的经历。故而大量查阅和记录一些内容,目的是总结规整一些内容,以便于建立基本认识,以及未来若用到可以快速查询,不再需要紧急的大量搜寻零散的知识。

一、Hadoop 原理和核心

  1. HDFS (Hadoop Distributed File System)

    • HDFS 是一种分布式文件系统,设计用于在大规模集群上运行。
    • 它将数据分片成固定大小的块(通常是 128MB 或 256MB),并将这些块分布存储在集群的不同节点上。
    • HDFS 提供高容错性,数据被复制多份以保证可靠性。
  2. MapReduce

    • 一种编程模型,用于处理大规模数据集。
    • 包括两个主要步骤:Map 和 Reduce。
      • Map:将输入数据转换为键值对。输入数据被分割成多个数据块(通常对应于 HDFS 中的数据块),每个数据块由独立的 Map 任务处理,生成中间结果。
      • Reduce:对具有相同键的所有值进行合并和处理,中间结果被合并以生成最终输出结果。
  3. YARN (Yet Another Resource Negotiator)

    • 资源管理平台,负责任务调度和集群资源管理。

二、Hadoop 应用中的 Java 代码示例

假设我们要用 Hadoop 统计一组文本文件中每个单词出现的次数,这是 Hadoop 的经典案例之一:词频统计 (WordCount)。

基本pom

在使用 Hadoop 进行 MapReduce 编程时,你需要在 pom 文件中引入一些必要的依赖来支持你的项目。这些依赖包括 Hadoop 核心库和 MapReduce 相关的库。
以下是一个基本的配置样例

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>hadoop-mapreduce-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <hadoop.version>3.3.4</hadoop.version> <!-- 这里可以根据需要调整Hadoop版本 -->
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- Hadoop Common -->
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>${hadoop.version}</version>
        </dependency>

        <!-- Hadoop MapReduce Client Core -->
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-core</artifactId>
            <version>${hadoop.version}</version>
        </dependency>

        <!-- Hadoop MapReduce Client Common -->
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-common</artifactId>
            <version>${hadoop.version}</version>
        </dependency>
        
        <!-- Hadoop MapReduce Client App -->
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-app</artifactId>
            <version>${hadoop.version}</version>
        </dependency>

        <!-- Hadoop MapReduce Client Job Client -->
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-mapreduce-client-jobclient</artifactId>
            <version>${hadoop.version}</version>
        </dependency>

    </dependencies>

</project>

注意事项:

  1. Hadoop版本:确保将 <hadoop.version> 替换为适合你的 Hadoop 集群的版本。使用不匹配的版本可能导致兼容性问题。

  2. Java版本:Hadoop 3.x 通常需要至少 Java 8,因此确保你的 maven.compiler.sourcemaven.compiler.target 设置为 1.8 或更高。

  3. 覆盖默认配置:在某些情况下,你可能需要进一步添加或覆盖其他依赖,例如 hadoop-hdfshadoop-yarn-apihadoop-yarn-common 等,具体取决于应用的具体功能和需要。

  4. 使用特定的Hadoop发布版本:如果您使用的是特定发行版(例如 Cloudera、Hortonworks),您可能需要使用这些发行版特定的依赖项。

确保在运行项目之前,Hadoop 已正确配置和安装在你的环境中。

1. 相关包导入

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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;

2. 实现 Mapper 类

/* 
Mapper<Object, Text, Text, IntWritable>可以看作是两个键值对 k1,v1,k2,v2
输入键值对 (k1 -> v1):
Object : 输入键(k1),通常是输入数据的偏移量或一些不需要处理的标识。它在大多数情况下不直接参与业务逻辑。
Text : 输入值(v1),是实际的输入数据内容,例如文本文件中的一行数据。
输出键值对 (k2 -> v2):
Text : 输出键(k2),实际需要输出和处理的业务逻辑相关的键。例如,在单词计数程序中,这个键可能是从输入文本中解析出来的单词。
IntWritable : 输出值(v2),通常对应于与输出键相关联的统计数据或结果值。例如,单词计数程序中每个单词的初始计数值通常为 1。( Hadoop 提供了自己的序列化和数据类型系统,不使用 Java 的基础类型(如 int))

这种设计允许 Mapper 从原始输入数据中提取有意义的信息,并将其转换为适合 Reducer 进一步处理的格式。
在集群环境中,每个 Mapper 实例可以并行处理输入数据,这种键值对的设计是 Hadoop 提供高度可扩展性和灵活性的基础之一。
*/
public static class TokenizerMapper extends Mapper<Object, Text, Text, IntWritable> {

    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();

    public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
        StringTokenizer itr = new StringTokenizer(value.toString());
        while (itr.hasMoreTokens()) {
            word.set(itr.nextToken());
            context.write(word, one);
        }
    }
}
/*
在 Hadoop 的 MapReduce 编程模型中,`Context` 对象起着至关重要的作用。它充当 MapReduce 程序中 Mapper 和 Reducer 之间,以及每个阶段与框架本身之间的桥梁。以下是 `Context` 的几个主要功能:

1. **配置访问**:
   - `Context` 提供对作业配置信息的访问。你可以通过 `context.getConfiguration()` 来获取作业配置,读取设定的参数和环境设置。

2. **输出写入**:
   - 在 Mapper 中,`Context` 用来输出键值对给 Reducer。这通常是通过 `context.write(key, value)` 方法来实现的。
   - 在 Reducer 中,同样使用 `context.write(key, value)` 方法来输出最终的结果。

3. **报告状态**:
   - `Context` 提供了一种机制允许 Mapper 和 Reducer 向 Hadoop 框架报告其进度以及自定义的状态信息。通过 `context.setStatus("current status")` 设置当前状态,通过 `context.progress()` 更新进度。

4. **计数器**:
   - `Context` 允许用户定义和更新计数器,这可以用来用于作业的实时监控和调试。你可以使用 `context.getCounter("GroupName", "CounterName").increment(1)` 来递增一个计数器。

5. **数据传递**:
   - `Context` 也是在各个任务中传递数据的媒介,包括将 Mapper 的输出传递到 Reducer。

综上所述,`Context` 对象提供了一系列方法,帮助开发人员在 Hadoop MapReduce 作业中轻松处理配置、数据输出、任务状态更新和计数等操作。
*/

Mapper<Object, Text, Text, IntWritable>:Mapper 类具有输入键/值类型 和 输出键/值类型。在这个例子中,输入是文本行,输出是单词及其计数。

  • 其中的泛型可以自己定义么?
    • 输入的键和值泛型(对于 Mapper 的输入)
      1. 输入键(第一对泛型中的第一个参数)通常是 LongWritable 类型,表示文件中的字节偏移量,但这并不是强制的。这取决于你使用的 InputFormat。
      2. 输入值(第一对泛型中的第二个参数)通常是 Text 类型,用于表示读取的一行文本或其他格式的数据。然而,你可以根据输入数据源的类型选择其他合适的类型(如 IntWritableBytesWritable 等)。
    • 输出的键和值泛型(对于 Mapper 和 Reducer 的输出)
      1. 输出键可以是任何实现了 Hadoop 的 WritableComparable 接口的类。Text 类型常被用于字符串键,但如果你的应用需要不同的键类型,你可以用其他的合适类型。
      2. 输出值可以是任何实现了 Writable 接口的类。常见的类型有 IntWritableLongWritableFloatWritable 等,具体取决于你的处理结果的需求。
    • 如果自定义类型
      如果你需要自定义类型(不满足现有的Writable接口),需要创建一个类来实现 Writable 或 WritableComparable 接口,并实现相应的接口方法,如 write(DataOutput out)readFields(DataInput in),以及 compareTo(Object o)(如果可比较)。

map 方法:对于输入的每一行(value),将其分割成单词,并将每个单词映射为 <单词, 1>,输出到 context。

3. 实现 Reducer 类

public static class IntSumReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
    private IntWritable result = new IntWritable();

    public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        int sum = 0;
        for (IntWritable val : values) {
            sum += val.get();
        }
        result.set(sum);
        context.write(key, result);
    }
}

Reducer<Text, IntWritable, Text, IntWritable>: Reducer 类定义了输入和输出的键/值类型。
reduce 方法: 对于输入的每个单词,将所有的计数值汇总,输出 <单词, 总计数>。

4. 配置作业并运行

public static void main(String[] args) throws Exception {
	
	//Job 对象中的设置是专门为当前 MapReduce 作业量身定制的。这意味着这些设置只会影响到这个作业本身的执行,而不会对其它作业造成影响。
	//你可以继承一些全局Configuration 的配置,比如集群设置、调度器配置等
    Configuration conf = new Configuration();
    Job job = Job.getInstance(conf, "word count");

    job.setJarByClass(WordCount.class);
    job.setMapperClass(TokenizerMapper.class);
    job.setCombinerClass(IntSumReducer.class); // 本地预处理减少网络传输, 这个可选用,底下的拓展标题内会讲到
    job.setReducerClass(IntSumReducer.class);

    job.setOutputKeyClass(Text.class);
    job.setOutputValueClass(IntWritable.class);

    FileInputFormat.addInputPath(job, new Path(args[0]));
    FileOutputFormat.setOutputPath(job, new Path(args[1]));

    System.exit(job.waitForCompletion(true) ? 0 : 1);
}

Job job = Job.getInstance(conf, "word count"): 创建一个新的作业并命名。
setJarByClass(WordCount.class):设置 JAR 文件内容。
setMapperClass 和 setReducerClass:指定 Mapper 和 Reducer 类。
addInputPath 和 setOutputPath:指定输入和输出路径。
job.waitForCompletion(true):提交作业并等待其完成。

三、总结

Hadoop MapReduce 是一个分布式计算框架,执行过程主要分为两个阶段:Map 和 Reduce。以下是一个典型的 MapReduce 作业从开始到结束的执行步骤:

1. 作业初始化

  • 作业提交:用户通过客户端提交一个 MapReduce 作业,这包括应用程序的代码和作业的配置。
  • 作业分配:Hadoop 集群中的 ResourceManager 负责接受作业并寻找一个合适的 NodeManager 来运行作业的 ApplicationMaster。

2. 输入数据分片(Splitting)

  • 作业提交后,Hadoop 会首先根据输入数据集创建输入分片(Splits),这通常依赖于 HDFS 的块大小。
  • 每个输入分片通常对应于一个 Mapper 任务。

3. Map 阶段

  • Mapper 执行:每个分片的数据被读取并由 Mapper 任务进行处理。Mapper 接受输入键值对,并输出中间键值对context.write(key, value)。
  • 本地化存储:中间键值对会被暂时存储在 TaskTracker 本地,并按照键进行分区和排序。这一阶段被称为“Shuffle and Sort”。

4. Shuffle and Sort(洗牌排序)

  • Shuffle:这是数据重分布的过程。Hadoop 会根据每个中间键的哈希值将中间结果分散到不同的 Reducer 节点上。这个分配过程确保同一键的所有对应值都被路由到同一个 Reducer。
  • Sort:在数据到达 Reducer 节点之前,Hadoop 对中间结果按键进行排序。这种排序确保 Reducer 处理时同类键的数据是有序的。

5. Reduce 阶段

  • Reducer 执行:Reducer 接收到排序后的键值对组,并对每个键的一组数据进行处理,通常会进行汇总、过滤或转换。(每个 Reducer 接收到的是某些中间键的一组键和对应的值集合,Hadoop 自动完成了这部分的键值对分组和传递,其实就是Mapper最后写入到context的中间键值对被分组等处理后的)
  • Reducer 程序中,reduce 方法接受三个参数:
    key:当前处理的键。
    values:对应于该键的所有值的迭代器。
    context:用于输出最终结果的上下文
  • Reducer 的输出是最终的计算结果,这是另一个键值对的集合。

6. 输出阶段

  • 由 Reducer 输出的结果会被存储到 HDFS 或其他配置的输出目的地。

7. 作业完成

  • 当所有 Reducer 任务完成后,整个 MapReduce 作业也宣告完成。
  • ApplicationMaster 向 ResourceManager 汇报任务完成,释放资源。

注意事项

  • Fault Tolerance:MapReduce 框架提供容错机制,能够在任务失败时重试任务,并在必要时重新调度到不同的节点。
  • 数据本地性:为了提升效率,尽量将 Mapper 任务调度到包含其输入数据的节点上,以减少数据传输开销。
  • 并行性:每个 Map 和 Reduce 阶段的任务可以独立并行执行,只要集群资源允许。

这种流程通过多次并行的数据处理和计算,能够高效地在分布式环境下处理大规模数据集。MapReduce 框架通过抽象化这种过程,使得开发者可以专注于定义如何进行键值对的转换和处理,而不必担忧底层的并行处理复杂性。

四、拓展

在 Hadoop MapReduce 架构中,Mapper 和 Reducer 的映射和分配是由框架自动管理的,确保了正确的数据流动和处理逻辑。为了处理不同的逻辑,你需要设置作业的配置,以及正确地将数据传递给目标 Reducer。这就是 MapReduce 作业的设计中的一些关键要素:

1. 作业配置

每一个 MapReduce 作业都有一个作业配置(Job Configuration),在这个配置中你可以指定:

  • 使用哪个 Mapper 类。
  • 使用哪个 Reducer 类。
  • Mapper 的输出键值类型。
  • Reducer 的输出键值类型。
  • 分区器(Partitioner)的算法,可以控制如何将键分配给 Reducer。

单独的逻辑应通过为不同任务定义独立的 Mapper 和 Reducer 类来处理。

2. 自定义分区器(Partitioner)

如果需要特定的逻辑将某些键值对分配给特定的 Reducer,您可以实现自定义分区器(Partitioner)。默认情况下,Hadoop 使用键的哈希值进行分区,通过将其除以 Reducer 的数量来决定数据发送到哪个 Reducer。

定义一个自定义分区器可以让你使用自己的规则:

public class MyCustomPartitioner extends Partitioner<Text, IntWritable> {
    @Override
    public int getPartition(Text key, IntWritable value, int numReduceTasks) {
        // 自定义逻辑来确定键发送到哪个 Reducer,这里只是一个示例
        return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
    }
}

3. Multiple Outputs

Hadoop 支持多输出(Multiple Outputs),从而允许同一个 Reducer 以不同的方式输出数据。你可以定义多个命名输出,并根据业务逻辑将数据写入不同的输出:

MultipleOutputs.addNamedOutput(job, "wordcount", TextOutputFormat.class, Text.class, IntWritable.class);

4. 控制逻辑

在 Reducer 中,控制逻辑确保你按照特定的处理方式处理不同的数据:

public void reduce(Text key, Iterable<IntWritable> values, Context context) {
    if (key.toString().startsWith("wordcount")) {
        // word count 特定的逻辑
    } else {
        // 其他逻辑
    }
}

通过使用这些技术,Hadoop MapReduce 可以处理复杂的逻辑流,并确保不同的 Mapper 和 Reducer 之间正确地传递数据。总的来说,使用正确的配置、分区器和业务逻辑实现,可以确保特定 Reducer 处理关联的 Mapper 输出。

5. 那什么是 Combiner?

在 Hadoop 的 MapReduce 框架中,流程一般是这样的:

  1. Mapper:处理输入数据并生成键值对(key-value pairs)。
  2. Reducer:接收来自不同 Mapper 的键值对并进行进一步的处理。

Combiner 是一个可选的步骤,它作用于 Mapper 和 Reducer 之间。它在每个 Map Task 的输出结果上本地执行一些简单的聚合操作(比如求和、计数等),其目的是减少要传输给 Reducer 的数据量。

为什么使用 Combiner?

  1. 减少网络传输量:在大型集群中,网络带宽可能是一个瓶颈。Combiner 可以在本地先对 Mapper 的输出进行部分合并,这样要通过网络传输到 Reducer 的数据就会减少。

  2. 提高程序效率:通过减少中间数据量,Combiner 可以减小系统的 I/O 负载以及提升 MapReduce 作业的整体执行速度。

比如上述例子的 IntSumReducer 作为 Combiner

假设我们在做一个“单词计数”的任务:

  • Mapper 输出每个单词和其出现的次数,类似这种形式:<word, 1>
  • Reducer 负责统计每个单词出现的总次数。

在加入 Combiner 的情况下:

  • Combiner 会在 Mapper 本地统计每个单词的出现次数,然后再把这个部分结果传递给 Reducer。例如,如果某个 Mapper 输出了 3 个 cat,Combiner 会把这 3 个 cat 先合并为 <cat, 3>
  • 这样,传送给 Reducer 的数据多了一层聚合,能够减少向 Reducer 发出的数据量,优化性能。

确保使用 Combiner 的场景

Combiner 并不是必须的,也不是一直有效。它适用的情境是 Combiner 和最终 Reducer 所做的逻辑是相同的(即满足结合性和交换性原则)。这通常是像求和、计数这样简单的聚合操作。

五、其他应用场景

当然,Hadoop 及其生态系统在各种大数据处理场景中都有广泛的应用。以下是一些常见的应用场景:

1. 数据存储与管理

  • 数据湖:Hadoop 可以作为数据湖的基础架构,存储多种类型的数据(结构化、半结构化和非结构化)。HDFS 提供了可扩展的、经过复制的增强数据存储能力。

2. 数据分析

  • 批处理数据分析:利用 MapReduce 来处理和分析大批量数据。例如,日志分析、点击流分析、客户行为分析等。
  • 机器学习:借助像 Apache Mahout 和 MLlib(Spark 的机器学习库)这样的工具来构建和训练大规模机器学习模型。

3. 数据集成

  • ETL (Extract, Transform, Load):Hadoop 可以用于大规模数据的提取、转换和加载。工具如 Apache Hive 和 Apache Pig 提供了更简单的数据处理语言来执行复杂的 ETL 作业。

4. 实时数据处理

  • 实时流处理:尽管 Hadoop 的核心主要是批处理,结合如 Apache Storm 或 Apache Kafka(用于数据流的入流架构)可以处理实时数据流。

5. 商业智能 (BI)

  • 数据仓库解决方案:Hadoop 是许多现代数据仓库解决方案的一部分,能够支撑 BI 工具的有效操作。通过 Hive 和类似工具,可以用 SQL 查询存储在 Hadoop 中的数据。

6. 搜索引擎

  • 索引和搜索:Hadoop 可以用于处理和索引海量数据以构建搜索引擎。例如,Apache Solr 和 Elasticsearch 可以在 Hadoop 生态系统中运行来提供强大的搜索和索引功能。

7. 算法与建模

  • 图计算:通过 Apache Giraph 实现大规模图计算,用于社交网络分析、网络爬虫、推荐系统等。

8. 企业应用

  • 风险管理和欺诈检测:许多金融机构使用 Hadoop 架构来实时分析交易数据,以检测和预防欺诈。

9. IOT 数据处理

  • 物联网数据处理:随着物联网设备的普及,Hadoop 可以帮助处理和存储来自这些设备的海量数据。

10. 基因组学

  • 生物信息学分析:利用 Hadoop 来处理和分析大型基因组数据集,为医学研究和临床应用提供支持。
posted @ 2025-01-09 14:46  J九木  阅读(87)  评论(0)    收藏  举报