统计文件中每一个字符出现的次数
1.准备一个文件,它可以存储在本地目录上,hdfs上,或ftp上都行
原理:搬砖有20000块砖,有10个人,放到仓库里,先划分任务量(Split),逻辑分。
MapReduce在刚开始的时候,会对文件进行切片(Split)处理。切分完成之后,每一个Split会交给一个单独的MapTask来处理
Split和Block
切片:Split,本质上是一种逻辑切分,切片之后,每一个切片会交给一个单独的子任务(MapTask来处理)
切块:Block,本质上是一种物理切分,切分之后,每一个切块会交给某一个DataNode来存储
如果没有指定,默认情况下,Split和Block等大
MapTask拿到切片之后,默认会对数据逐行处理。MapTask之间,只是处理的数据不同,但处理逻辑是相同的
在Reduce阶段刚刚开始的时候,会先将相同的键对应的值放到一块去,形成一个迭代器,这个过程称之为分组(group)
如下图
准备一篇英文文章en.txt
上传到hdfs上
hadoop fs -put /yinwen.txt /
注意这是hadoop的hdfs可视化界面,端口9870,hadoop3.X的端口,hadoop2.X是50070
浏览器输入m1:9870
代码
引入pom.xml依赖
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>3.1.3</version>
</dependency>
Mapper代码:
package count;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
// 用于完成Map阶段
// 再MapReduce中,要求被处理的数据能够被序列化
// MApReduce提供了一套单独的序列化机制
// KEYIN-输入的键的类型。如果不指定,默认情况下,表示行的字节偏移量 KEYIN中long不可序列化,所以提供了LongWritable 可序列化机制
// VALUEIN-输入值得类型。如果不指定,默认情况下,表示的读取到的一行数据
// KEYOUT-输出的键的类型。当前案例中,输出的键表示的是字符
// VALUEOUT-输出的值的类型。当前案例,输出的值表示的是次数
public class CharCountMapper extends Mapper<LongWritable,Text, Text,LongWritable> {
private final LongWritable once = new LongWritable(1);
// 覆盖map方法,将处理逻辑写到这个方法中
// key:键。表示的是行的字节偏移量
// value:值。表示读取到的一行数据
// context:配置参数
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
// 将一行数据中的字符拆分出来
char[] cs = value.toString().toCharArray();
// 数据是hello,拆分出来的数组中包含的就是{'h','r','l','l','o'}
// 可以写出h:1 e:1 l:1 l:1 o:1
for (char c:cs){
context.write(new Text(c+""),once);
}
}
}
KEYIN,VALUEIN
reducer代码:
package count;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
// KEYIN、VALUEIN输入的键的类型。
// Reducer的数据从Mapper来的
// 所以Mapper的输出就是Reducer的输入
// KEYOUT、VALUEOUT-输出的值的类型。当前案例中,要输出每一个字符对应的总次数
public class CharCountReducer extends Reducer<Text, LongWritable,Text,LongWritable> {
// 覆盖reduce方法,将计算逻辑写到这个方法中
// key:键。当前案例中,键是字符
// values:值。当前案例中,值是次数的集合对应的迭代器
// context:配置参数
@Override
protected void reduce(Text key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
// key='a'
// value={1,1,1,1,1,1,1...}
// 定义变量来记录总次数
int sum=0;
for (LongWritable value:values){
sum+=value.get();
}
context.write(key,new LongWritable(sum));
}
}
Driver代码:
package count;
import org.apache.hadoop.conf.Configuration;
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.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.io.IOException;
public class CharCountDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
// 构建环境变量
Configuration conf = new Configuration();
// 构建任务
Job job = Job.getInstance(conf);
// 设置入口类
job.setJarByClass(CharCountDriver.class);
// 设置Mapper类
job.setMapperClass(CharCountMapper.class);
// 设置Reducer类
job.setReducerClass(CharCountReducer.class);
// 设置Mapper的输出类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(LongWritable.class);
// 设置Reduce的输出类型
job.setOutputKeyClass(Text.class);
job.setOutputKeyClass(LongWritable.class);
// 设置输入路径
FileInputFormat.addInputPath(job, new Path("hdfs://m1:9000/yinwen.txt"));
// 设置输出路径-要求输出路径必须不存在
FileOutputFormat.setOutputPath(job,new Path("hdfs://m1:9000/result/"));
// 提交任务
job.waitForCompletion(true);
}
}
使用idea打包,上传到linux服务器上
执行
hadoop jar /hadoopmapreduce-1.0-SNAPSHOT.jar count.CharCountDriver
使用HDFS EXPLORER查看结果
也可使用浏览器查看
总结mapper执行流程如下:
- Input:读取文件内容。
- Split:将每行数据按照空格进行拆分。
- Map:分别计算每行每个单词出现的次数,key 是单词,value 为 1(表示 1 个单词)。
- Shuffle:分区和排序,将 Map 输出中所有 key 相同的部分汇总到一起,作为一个 Reduce 的输入。
- Reduce:把 key 相同的数据进行累计,得到每个单词出现的次数。
- Output:输出结果到文件。