目录
[MapReduce join两个表的流程?](#MapReduce join两个表的流程?)
shuffle为什么要排序?
在MapReduce的Shuffle过程中排序数据的原因主要有以下几点:
1、数据聚合:
在Reduce阶段,需要对具有相同键的所有值进行聚合或汇总操作。为了确保所有具有相同键的键值对能够被同一
个Reduce任务处理,排序是必需的。排序确保了具有相同键的记录会被连续地存储和处理,从而可以正确地执行
聚合操作。
2、简化Reduce操作:
排序后的数据流使得Reduce函数能够更容易地识别和处理具有相同键的记录。如果数据未排序,Reduce函数将不
得不维护一个复杂的结构来跟踪和聚合所有相关的键值对。
3、提高效率:
排序有助于减少在Reduce阶段的处理时间和内存使用。因为数据已经排序,Reduce任务可以简单地遍历键值对,
每当遇到新的键时,就处理完当前键的聚合操作,然后继续下一个键的处理。
4、优化数据传输:
在Shuffle过程中,排序可以帮助减少网络传输的数据量。由于数据是按键排序的,可以使用更高效的编码方案,
比如游程编码(Run-Length Encoding, RLE),来表示重复的键值对,从而减少传输的数据量。
5、容错和恢复:
排序还有助于在Shuffle过程中出现故障时的恢复。如果数据是排序的,那么在失败的情况下,可以从故障点开始
重新处理,而不需要处理整个数据集。
6、一致性:
排序保证了每次运行MapReduce作业时,对于相同的输入,输出结果是一致的。这对于调试和验证结果非常重
要。
然而,在某些场景下,排序可能不是必需的。例如,在Apache Spark中,Shuffle过程默认不进行排序,除非
用户明确要求(例如,通过sortByKey()操作)。这是因为强制排序可能会增加额外的计算开销,而并非所有操作
都需要数据排序。
总的来说,排序在MapReduce的Shuffle过程中是为了解决数据聚合的需求,同时提高处理效率和数据传输的优
化。但在现代分布式计算框架中,排序是否被执行取决于具体的应用需求和性能考量。
说一下map是怎么到reduce的?
在MapReduce编程模型中,从Map阶段到Reduce阶段的过程可以概括如下:
1、Map阶段:
Map阶段的主要任务是将输入数据(通常是一组键值对)通过Map函数转换成另一组键值对。这些键值对随后会被
MapReduce框架收集并排序。
Map函数接受一个输入键值对,并产生零个或多个输出键值对。输出键值对的数量、类型和内容完全取决于Map函
数的实现。
在这个过程中,每个输入键值对都是独立处理的,Map阶段中的任务通常是高度并行的。
2、Shuffle和Sort阶段:
在Map阶段之后,MapReduce框架会对Map输出的键值对进行Shuffle(洗牌)和Sort(排序)操作。
Shuffle阶段主要是将Map输出的键值对按照键进行分组,使得具有相同键的所有值被聚集在一起。
Sort阶段则是对每个键对应的值列表进行排序。这个排序操作是可选的,取决于Reduce函数是否需要有序的输
入。
3、Reduce阶段:
Reduce阶段的任务是处理Shuffle和Sort阶段输出的键值对。
Reduce函数接受一个键和该键对应的值列表作为输入,并产生零个或多个输出键值对。输出键值对的数量、类型
和内容完全取决于Reduce函数的实现。
Reduce函数对每个唯一的键及其对应的值列表执行操作,并产生相应的输出。由于每个键只传递给一个Reduce任
务,因此Reduce阶段的任务通常是顺序执行的。
总结起来,从Map阶段到Reduce阶段的过程主要包括:Map函数处理输入数据并产生输出键值对,MapReduce框
架对输出键值对进行Shuffle和Sort操作,然后Reduce函数处理排序后的键值对并产生最终结果。这个过程是
MapReduce编程模型的核心,它允许用户以并行和分布式的方式处理大规模数据集。
说一下你了解的用哪几种shuffle机制?
在分布式计算框架中,尤其是处理大规模数据集时,Shuffle机制是一个关键的性能影响因素。不同的框架采用了
不同的Shuffle机制来优化数据重分布过程。以下是一些主要的Shuffle机制:
1、MapReduce Shuffle:
Apache Hadoop MapReduce使用了一种经典的Shuffle机制,它涉及到Map端的溢写、合并、排序和分区,以
及Reduce端的拉取、解码、合并和排序。Map任务将输出数据根据键的哈希值分区,并且在溢出文件生成前进行局部排序。Reduce任务从Map任务拉取数据,进行全局排序,然后执行Reduce操作。
2、Spark Shuffle:
Apache Spark采用了一种更为灵活的Shuffle机制,它使用了一种称为"shuffle handler"的组件来管理
Shuffle过程。Spark的Shuffle过程通常不会进行全局排序,除非用户明确请求(例如使用sortByKey())。
Spark使用了一种称为"ShuffleMapTask"的任务来处理Shuffle过程,并且支持多种Shuffle算法,如Hash
Shuffle和Sort Shuffle。
3、Flink Shuffle:
Apache Flink的Shuffle机制依赖于其流处理特性。在Flink中,Shuffle通常指的是重新分区操作,它可以
是分区、排序或两者结合。Flink的Shuffle优化了网络传输和内存使用,提供了高性能的流和批处理
Shuffle。
4、Tez Shuffle:
Apache Tez是一种用于复杂数据处理的框架,它改进了MapReduce的Shuffle机制,提供了更灵活的数据流模
型。Tez的Shuffle机制允许在同一个作业中进行多次Shuffle操作,并且优化了数据重分布过程,减少了磁盘
I/O和网络传输。
6、Spark Tungsten Shuffle:
Spark的Tungsten项目引入了一种更高效的Shuffle机制,它通过减少数据序列化/反序列化的开销、利用硬件
加速和内存管理技术来提升性能。Tungsten Shuffle优化了内存使用和CPU缓存使用,提高了Shuffle的效
率。
7、Hadoop 2.0的Shuffle改进:
Hadoop 2.0引入了YARN(Yet Another Resource Negotiator)资源管理系统,它改进了Shuffle过程,
提供了更好的资源管理和任务调度。Hadoop 2.0的Shuffle机制还优化了数据压缩和传输,以减少网络延迟。
8、Presto Shuffle:
Presto是Facebook开发的分布式SQL查询引擎,它的Shuffle机制专注于低延迟和高吞吐量。Presto的Shuffle优化了内存使用和网络传输,适用于大规模数据分析。
不同的框架和版本可能有不同的Shuffle实现细节,但上述机制涵盖了大多数分布式计算系统中常见的Shuffle
处理方式。每种Shuffle机制都有其特定的优化点和适用场景,选择合适的Shuffle机制对于提高分布式计算的
性能至关重要。
MapReduce的数据处理过程
MapReduce的数据处理过程可以清晰地分为几个阶段,每个阶段都有其特定的功能和任务。以下是MapReduce的
数据处理过程的详细解释:
1、Input阶段(输入阶段):
此阶段主要是从Hadoop分布式文件系统(HDFS)或其他数据源中读取数据。
数据被切分成多个小数据块,每个数据块称为一个输入split(分片)。默认情况下,每个split的大小与HDFS中
的block大小相同,通常为128MB。
分片后的数据作为Map阶段的输入。
2、Map阶段:
Map阶段的任务是将输入split中的数据通过自定义的Map函数进行处理。
Map函数读取一个输入键值对(在Hadoop中,键通常是数据在文件中的偏移量,值则是对应的数据行),并生成一个或多个中间键值对。
这些中间键值对会被写入本地磁盘的临时缓冲区中,等待后续的Shuffle阶段处理。
3、Shuffle阶段:
Shuffle阶段是对Map阶段输出的中间键值对进行一系列操作,以准备给Reduce阶段作为输入。
分区(Partition):根据Map输出的键,使用哈希函数将数据分发到不同的Reduce任务中。默认情况下,使用哈希取模的方式确定键值对应该发送到哪个Reduce任务。
排序(Sort):在数据被写入磁盘之前,对相同键的值进行排序。这有助于Reduce阶段更高效地处理数据。
合并(Combine):这是一个可选步骤,用于在发送数据到Reduce任务之前,对具有相同键的值进行局部聚合,以减少数据传输量。
溢写(Spill):当缓冲区中的数据达到一定的阈值(如80%)时,数据会被写入磁盘的临时文件中。这个过程可能会多次发生,生成多个临时文件。
合并临时文件(Merge):在Map任务完成后,所有的临时文件会被合并成一个或多个最终文件,这些文件将作为Reduce阶段的输入。
4、Reduce阶段:
Reduce阶段的任务是处理Shuffle阶段输出的中间键值对,并生成最终的输出结果。
Reduce函数接收一个键和对应的值列表(即所有具有相同键的值的集合),并根据自定义的逻辑对这些值进行处
理。
Reduce函数输出的结果会被写入到HDFS或其他指定的输出位置。
5、Output阶段(输出阶段):
Reduce阶段处理完成后的数据,通过OutputFormat接口指定的输出格式,写入到HDFS或其他指定的存储系统中。
在整个MapReduce过程中,数据的处理是高度并行化的。多个Map任务可以并行处理不同的输入split,而多个Reduce任务也可以并行处理不同的键。这种并行处理的能力使得MapReduce能够高效地处理大规模数据集。
mapjoin的原理(实现)?应用场景?
MapJoin的原理和实现
原理
MapJoin的基本思想是在Map阶段就完成两个表的连接,而不是等到Reduce阶段。它适用于一个大表和一个小表
的连接场景,其中小表可以完全加载到内存中。这是因为如果两个都是大表,那么在每个Map任务中加载整个小表
到内存中可能不是最优的策略,因为内存消耗可能会变得非常高。
实现步骤
小表加载到内存:首先,MapJoin会识别哪个表是小表,这个小表会被读入内存并构建一个哈希表
(HashTable)。在Hive中,这个阈值可以通过配置参数hive.auto.convert.join.noconditionaltask.size来控制,默认为25MB。
哈希表的序列化与分发:构建好的哈希表会被序列化并压缩成文件,然后这些文件被复制到所有执行Map任务的节
点的分布式缓存(DistributedCache)中。
Map阶段连接:在每个Map任务中,小表的哈希表文件会被反序列化回内存中。然后,Map任务会扫描大表的数
据,查找与哈希表中键匹配的行,并立即产生连接后的结果,避免了Shuffle阶段的网络传输。
应用场景
MapJoin特别适合以下几种情况:
数据倾斜:当连接操作涉及的表中有一个表非常小,而另一个表非常大时,使用MapJoin可以显著减少处理时间,
因为小表可以被完全加载到内存中,避免了数据倾斜问题。
频繁查询小表:如果一个查询需要多次连接到一个固定的小表,MapJoin可以减少每次连接的开销,提高查询效
率。
实时或近实时查询:在需要快速响应的查询场景下,MapJoin可以减少等待时间,因为它减少了Shuffle阶段的
延迟。
减少网络带宽使用:MapJoin减少了数据在网络间的传输,这对于网络带宽有限的情况特别有利。
需要注意的是,虽然MapJoin可以带来显著的性能提升,但它并不总是最佳的选择。如果小表的大小超过了可用内存,或者Map任务的数量非常多导致内存碎片化严重,那么MapJoin可能不会带来预期的性能优势。因此,在实际应用中,需要根据具体情况权衡是否使用MapJoin。在某些系统中,MapJoin可以通过特定的Hive注释(Hint)或配置参数来启用或禁用。例如,在Hive中,可以使用/*+ MAPJOIN(tables) */来显式地指定使用MapJoin。
reducejoin如何执行(原理)
Reduce Join的执行原理可以清晰地分为两个阶段:Map阶段和Reduce阶段。下面是每个阶段的具体解释:
1、Map阶段:
主要工作:为来自不同表或文件的key/value对打标签以区别不同来源的记录。
具体步骤:
1) 识别来自不同表或文件的key/value对。
2) 对这些key/value对打标签,通常通过添加一个新的字段或标志来区分它们的来源。
3) 使用连接字段(通常是两张表中相同的列)作为key,其余部分和新加的标志作为value。
4) 将这些标记过的key/value对输出。
2、Reduce阶段:
主要工作:在每个分组(基于Map阶段设定的连接字段作为key)中将来自不同文件的记录分开,并进行合并。
具体步骤:
1) 根据Map阶段输出的key进行分组。
2) 在每个分组中,根据Map阶段添加的标志来区分来自不同文件的记录。
3) 将这些记录进行合并,可能涉及笛卡尔积或其他形式的组合,具体取决于所需的连接类型。
4) 输出合并后的结果。
优点与缺点:
优点:Reduce Join是MapReduce框架实现表之间连接操作的一种常见方式,它允许在大数据集上进行高效的连
接操作。
缺点:由于所有数据都必须经过Shuffle过程(即Map和Reduce之间的数据传输阶段),这种方式可能会导致大量
的数据传输,从而降低效率。特别是在处理大规模数据集时,这种效率问题可能更加明显。
应用场景:
Reduce端连接比Map端连接更为普遍,因为在Map阶段可能无法获取所有需要的连接字段。即,同一个key对应的
字段可能位于不同的Map任务中,这使得在Reduce阶段进行连接更为合适。然而,这也意味着连接效率可能较
低,因为所有数据都需要经过Shuffle过程。
MapReduce为什么不能产生过多小文件
在MapReduce框架中,产生过多小文件会产生一系列问题,主要影响Hadoop分布式文件系统(HDFS)的性能和MapReduce作业的效率。以下是几个关键原因:
1、NameNode内存压力:
HDFS中的每个文件和目录的元数据都存储在NameNode的内存中。每个文件的元数据包括文件名、权限、块位置等
信息,大约占用150字节。如果存在大量小文件,即使这些文件的实际大小很小,也会导致NameNode的内存消耗
过大。这限制了NameNode能够管理的文件数量,从而影响了整个集群的可扩展性和稳定性。
2、Map任务过多:
MapReduce作业中的每个输入文件(或文件的一部分)通常都会触发一个Map任务。如果输入是许多小文件,那么
将会产生大量的Map任务,这会增加任务调度的开销。每个Map任务的启动都需要一定的初始化时间,包括JVM的
启动、类的加载等,这些开销在处理大量小文件时会累积成显著的延迟。
3、网络传输开销:
在Shuffle阶段,Map任务的输出需要在网络上传输给Reduce任务。如果Map任务产生的输出文件都很小,那么
网络传输的开销(如TCP/IP头部开销)相对于实际数据传输的比例会增大,降低整体的传输效率。
4、磁盘I/O开销:
访问小文件通常意味着更多的随机读写操作,这比顺序读写大文件效率要低。随机I/O操作需要更长的时间,尤其
是在机械硬盘上,寻道时间会成为瓶颈。
5、数据倾斜:
如果数据分布不均,某些Map任务处理的数据量远小于其他任务,那么在Shuffle和Reduce阶段可能会产生数据
倾斜,导致部分任务处理时间过长,而其他任务则闲置,降低了整体并行处理的效率。
为了避免这些问题,可以采取一些策略来处理小文件问题,例如:
使用SequenceFile或MapFile等二进制格式合并小文件,减少文件数量。
调整MapReduce作业的配置,如使用CombineFileInputFormat来合并小文件作为单个Map任务的输入。
在数据导入阶段进行预处理,合并小文件。
优化数据生成和存储策略,避免生成过多小文件。
通过这些策略,可以有效地减轻小文件对Hadoop集群的影响,提高MapReduce作业的执行效率。
MapReduce分区及作用
在MapReduce框架中,分区(Partitioning)是将Map阶段的输出数据分配给不同Reduce任务的关键过程。MapReduce采用"分而治之"的策略处理大规模数据集,其中分区机制确保了数据的合理分布,以便后续的Reduce
任务能够并行处理。
分区的作用:
1、数据分配:
分区决定了Map任务输出的中间结果将被送往哪个Reduce任务进行进一步处理。这是通过一个分区函数实现的,
它根据键(key)的值决定数据的归属。
2、数据一致性:
分区确保具有相同键(key)的所有键值对(key-value pairs)被发送到同一个Reduce任务。这对于那些需
要聚合相同键下的所有值的操作至关重要,比如求和、计数或平均值计算。
3、并行处理:
分区使得MapReduce能够并行地在多个节点上执行Reduce任务,每个节点处理一组特定的分区数据,从而提高了
处理大规模数据集的速度。
4、负载均衡:
理想的分区策略应当尽量使数据在各个Reduce任务之间均匀分布,以避免某些任务过载而其他任务空闲的情况,
即数据倾斜问题。
默认分区器:
MapReduce框架提供了默认的分区器HashPartitioner,它基于键(key)的哈希值与Reduce任务数进行取模运算,来确定数据应该分配到哪一个Reduce任务。具体公式如下:
𝑝 = ℎ𝑎𝑠ℎ(𝑘𝑒𝑦) mod 𝑛
其中
p 是分区编号,hash(key) 是键的哈希值,n 是Reduce任务的总数。
自定义分区器:
在某些场景下,可能需要根据特定的业务逻辑来定制分区策略,这时可以创建自定义的分区器。自定义分区器需要
继承Partitioner抽象类,并重写getPartition()方法。这个方法接收键和值作为参数,并返回一个整型值,
代表数据应该分配到的分区编号。
自定义分区器可以基于键的某个属性、键的前缀、地理位置或其他任何标准来实现数据的分区,以满足不同的数据
处理需求。
总结:
分区是MapReduce框架中一个核心概念,它对数据的分布和处理效率有重要影响。通过合理设计分区策略,可以
最大化MapReduce作业的性能和效果。
ReduceTask数量和分区数量关系
在MapReduce中,ReduceTask的数量和分区数量有着密切的联系,因为分区策略决定了Map任务的输出将如何被
分配给Reduce任务进行处理。以下是一些关键点,描述了ReduceTask数量和分区数量之间的关系:
1、默认情况下:
如果没有显式设置ReduceTask的数量,MapReduce框架默认只有一个ReduceTask。这意味着所有的分区数据
最终都会被这个单一的ReduceTask处理,无论有多少个分区。
2、分区与ReduceTask数量相等:
当ReduceTask的数量等于分区数量时,每个分区的数据将被分配给一个单独的ReduceTask。这是最常见和最理
想的场景,因为它可以充分利用并行处理能力,避免数据倾斜。
3、ReduceTask数量少于分区数量:
如果ReduceTask的数量小于分区数量,那么多个分区的数据将被合并后分配给较少的ReduceTask。这可能导致
某些ReduceTask处理的数据量大大超过其他ReduceTask,引起负载不平衡。
4、ReduceTask数量多于分区数量:
如果ReduceTask的数量大于分区数量,多余的ReduceTask将处于空闲状态,因为没有足够的分区数据供它们处
理。在这种情况下,即使有多个ReduceTask,实际上只会有分区数量那么多的ReduceTask在工作,其余的将不
接收任何数据。
5、自定义分区器:
当使用自定义分区器时,分区规则由程序员定义。分区器将键映射到一个特定的分区,而这个分区号将决定数据发
送给哪个ReduceTask。如果分区器的逻辑与ReduceTask的数量不匹配,可能会导致数据无法正确分配,甚至抛
出异常。
6、数据倾斜:
不当的ReduceTask数量或分区策略可能会导致数据倾斜,即一部分ReduceTask处理的数据量远超其他ReduceTask。这会导致处理时间的延长和资源的浪费。
为了确保MapReduce作业的高效执行,通常建议ReduceTask的数量等于或接近分区的数量,同时保证数据在各
个ReduceTask之间均匀分布。此外,可以根据数据特性和业务需求调整ReduceTask的数量和分区策略,以达到
最佳的并行处理效果。例如,在数据量很大时,可以增加ReduceTask的数量来充分利用集群的计算能力;而在数
据量较小或需要减少输出文件数量时,可以适当减少ReduceTask的数量。
Map的分片有多大
在Hadoop的MapReduce框架中,输入数据被分割成多个分片(splits),每个分片通常对应一个Map任务。分片
的大小默认情况下是基于HDFS(Hadoop Distributed File System)的数据块大小,这通常是64MB或
128MB,具体取决于Hadoop集群的配置。
分片大小的确定主要受到以下几个因素的影响:
1、HDFS块大小:HDFS块大小是Hadoop集群的一个配置参数,通常是64MB或128MB。分片大小一般不会超过
HDFS块大小。
2、mapred.min.split.size:这是一个配置参数,表示最小分片大小的默认值,通常是1字节,但实际大小会
根据File Input Format的不同而有所不同。
3、mapred.max.split.size:表示最大分片大小,其默认值为Long.MAX_VALUE,但在实际应用中,它通常
被限制在HDFS块大小之内。
4、mapred.map.tasks:虽然这个参数本身不直接影响分片大小,但它与分片的生成逻辑有关。分片的目标大小
(goal size)可以基于这个参数和HDFS块大小来计算,例如,如果mapred.map.tasks的值为2,那么目标分
片大小可能是HDFS块大小的一半。
5、文件大小和分布:实际分片大小还会受到输入文件大小和分布的影响。例如,如果一个文件的大小略大于一个
HDFS块,那么它可能仍然作为一个单独的分片处理,即使这意味着分片大小超过了目标分片大小。
6、分片算法:MapReduce框架使用的分片算法也可能影响分片的大小,有些算法可能会尝试让分片大小更加均
匀,而有些则可能基于文件的内部结构(如文本行数)来生成分片。
在实践中,为了减少网络传输和提高处理效率,分片大小往往被设置为等于HDFS块大小,这样可以确保每个Map任
务处理的数据都在本地节点上,减少跨节点的数据传输。然而,对于某些特殊类型的文件(如压缩文件),分片大
小可能会有所不同,以适应文件的压缩特性。
MapReduce join两个表的流程?
MapReduce 是一个编程模型,用于大规模数据集(大于1TB)的并行处理。在 MapReduce 中执行两个表的连
接(join)操作,通常涉及一系列的步骤,这些步骤会利用 Map 和 Reduce 阶段来分解和重组数据以执行连
接。
以下是使用 MapReduce 进行两个表连接的基本流程:
1. 数据准备
确保两个表(表A和表B)都已经被分割成可以并行处理的多个数据块(例如 HDFS 上的多个块)。
2. Map 阶段
读取数据:Map 函数从输入源(如 HDFS)读取表A和表B的数据。
数据标记:为每个记录添加一个标记,以指示它来自哪个表(例如,为表A的记录添加"A",为表B的记录添加
"B")。
键值对生成:Map 函数将连接键(通常是用于连接的字段,如 ID)作为键,将记录的其他部分(可能还有标记)
作为值,生成一系列的键值对。
3. Shuffle 阶段
排序和分组:MapReduce 框架会自动对 Map 阶段输出的键值对进行排序,并将具有相同键的值组合在一起。
4. Reduce 阶段
连接操作:Reduce 函数接收具有相同键的值的列表。它遍历这些值,并根据它们的标记(来自表A还是表B)来
执行连接操作。
结果输出:Reduce 函数将连接后的结果输出到输出源(如 HDFS)。
5. 优化和注意事项
1) 数据倾斜:当某个键的数据量远大于其他键时,可能会发生数据倾斜,导致某些 Reduce 任务处理的数据
量远大于其他任务。这可以通过对数据进行预分区(pre-partitioning)或使用更复杂的连接策略(如倾斜连
接)来解决。
2) 内存管理:由于 Reduce 任务需要处理大量的值(来自具有相同键的记录),因此必须小心管理内存使用,
以避免 OutOfMemoryError。
3) 选择合适的连接键:连接键的选择对性能至关重要。选择具有足够选择性的键(即具有不同值的键的数量)
可以减少需要比较的记录数量。
4) 使用组合器(Combiner):在某些情况下,可以在 Map 阶段使用组合器来减少发送到 Reduce 阶段的数
据量。组合器在 Map 阶段本地运行,并可以像 Reduce 函数一样处理具有相同键的值。
5) 广播小表:如果其中一个表(如表B)很小,可以将其完整地读入内存,并在 Reduce 阶段与表A的记录进
行连接。这通常被称为"广播连接"或"小表复制"。
6) 使用外部排序:对于非常大的数据集,可能需要在 MapReduce 之外使用外部排序工具(如 Terasort)
来对数据进行预排序,以减少 Shuffle 阶段的开销。
7) 使用专门的连接算法:对于某些特定的连接类型(如倾斜连接或等值连接),可以使用专门的连接算法来提
高性能。这些算法可能涉及数据的重新分区、索引或其他优化技术。
手撕一段简单的MapReduce程序
java
选择一个经典的例子:单词计数(Word Count)。下面我将展示一个使用Java编写的Word Count MapReduce
程序,该程序用于统计一组文本文件中每个单词出现的次数。
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
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;
// 定义Map任务
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
// 将每行文本拆分成单词
StringTokenizer tokenizer = new StringTokenizer(value.toString());
while (tokenizer.hasMoreTokens()) {
word.set(tokenizer.nextToken());
context.write(word, one);
}
}
}
// 定义Reduce任务
public class WordCountReducer 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);
}
}
// 主程序
public class WordCount {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf, "word count");
job.setJarByClass(WordCount.class);
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.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);
}
}
这段代码包括了三个主要部分:
WordCountMapper 类,它实现了 Mapper 接口,将输入的文本行拆分成单词,并为每个单词输出键值对,其
中键是单词,值是常量1。
WordCountReducer 类,它实现了 Reducer 接口,将所有具有相同键(即相同的单词)的值加总,得到单词
的总数。
WordCount 类,这是主程序,负责设置Job的参数,包括输入路径、输出路径,以及Mapper和Reducer类。
在实际部署和运行此程序之前,你需要确保你有一个Hadoop环境,并且已经设置了正确的输入和输出路径。此
外,你还需要将这个Java程序打包成JAR文件,并通过Hadoop的命令行接口来提交作业。
引用:https://www.nowcoder.com/discuss/353159520220291072
通义千问、文心一言