文章目录
一、实战概述
-
在这个实战中,我们使用了Hive框架来处理学生的月考成绩数据。首先,我们准备了三个文本文件,分别包含了语文、数学和英语的月考成绩数据。这些数据被上传到HDFS的指定目录。
-
接着,我们启动了
Hive Metastore
服务,并通过Hive客户端连接到Hive。在Hive中,我们创建了一个分区表student_score
,用于存储学生的成绩数据,其中分区字段为科目(subject)。 -
然后,我们按照科目将数据加载到分区表中,分别加载了语文、数学和英语的成绩数据。通过这样的分区方式,我们能够更方便地对数据进行查询和分析。
-
最后,我们使用Hive的SQL语句进行统计,计算每个学生在三个科目上的月考平均分。使用了AVG函数和ROUND函数来得到每个学生的平均分,并保留一位小数。这样,我们得到了每个学生在语文、数学和英语三个科目上的月考平均分的统计结果。
-
整个实战过程涉及了Hive的表创建、分区管理、数据加载和SQL查询等操作,展示了Hive在大数据处理中的灵活性和便捷性。通过这次实战,我们能够更好地理解和掌握Hive框架在数据分析和查询中的应用。
二、提出任务
- 语文月考成绩 -
chinese.txt
1 张晓云 89
2 张晓云 73
3 张晓云 67
4 张晓云 70
5 张晓云 79
6 张晓云 87
7 张晓云 99
8 张晓云 83
9 张晓云 97
10 张晓云 92
11 张晓云 67
12 张晓云 86
1 王东林 49
2 王东林 83
3 王东林 67
4 王东林 49
5 王东林 93
6 王东林 87
7 王东林 65
8 王东林 92
9 王东林 60
10 王东林 94
11 王东林 81
12 王东林 90
1 李宏宇 77
2 李宏宇 66
3 李宏宇 89
4 李宏宇 87
5 李宏宇 96
6 李宏宇 79
7 李宏宇 87
8 李宏宇 96
9 李宏宇 69
10 李宏宇 87
11 李宏宇 96
12 李宏宇 79
- 数学月考成绩 -
math.txt
1 张晓云 79
2 张晓云 83
3 张晓云 77
4 张晓云 90
5 张晓云 89
6 张晓云 67
7 张晓云 89
8 张晓云 93
9 张晓云 90
10 张晓云 82
11 张晓云 77
12 张晓云 96
1 王东林 78
2 王东林 94
3 王东林 76
4 王东林 70
5 王东林 90
6 王东林 83
7 王东林 85
8 王东林 82
9 王东林 84
10 王东林 78
11 王东林 99
12 王东林 93
1 李宏宇 86
2 李宏宇 81
3 李宏宇 76
4 李宏宇 93
5 李宏宇 88
6 李宏宇 82
7 李宏宇 81
8 李宏宇 93
9 李宏宇 86
10 李宏宇 90
11 李宏宇 67
12 李宏宇 88
- 英语月考成绩 -
english.txt
1 张晓云 78
2 张晓云 83
3 张晓云 92
4 张晓云 66
5 张晓云 82
6 张晓云 89
7 张晓云 79
8 张晓云 68
9 张晓云 96
10 张晓云 91
11 张晓云 87
12 张晓云 82
1 王东林 69
2 王东林 86
3 王东林 73
4 王东林 99
5 王东林 67
6 王东林 95
7 王东林 74
8 王东林 92
9 王东林 76
10 王东林 88
11 王东林 92
12 王东林 56
1 李宏宇 88
2 李宏宇 78
3 李宏宇 92
4 李宏宇 78
5 李宏宇 89
6 李宏宇 76
7 李宏宇 92
8 李宏宇 75
9 李宏宇 88
10 李宏宇 92
11 李宏宇 97
12 李宏宇 85
- 利用Hive框架,统计每个同学各科月考平均分
三、完成任务
(一)准备数据
- 启动hadoop服务
1、在虚拟机上创建文本文件
-
创建
subjectavg
目录,在里面创建chinese.txt
文件(数据没有显示全)
-
创建
math.txt
(数据没有显示全)
-
创建
english.txt
(数据没有显示全)
2、上传文件到HDFS指定目录
- 创建
/subjectavg/input
目录,执行命令:hdfs dfs -mkdir -p /subjectavg/input
- 将文本文件
chinese.txt
、math.txt
与english.txt
,上传到HDFS的/subjectavg/input
目录
(二)实现步骤
1、创建Maven项目
- Maven项目 -
SubjectAvg
- 单击【Finish】按钮
2、添加相关依赖
- 在
pom.xml
文件里添加hadoop
和junit
依赖
<dependencies>
<!--hadoop客户端-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>3.3.4</version>
</dependency>
<!--单元测试框架-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
</dependencies>
3、创建日志属性文件
- 在
resources
目录里创建log4j.properties
文件
log4j.rootLogger=INFO, stdout, logfile
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/subjectavg.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
4、创建学生实体类
- 在
net.hpy.mr
包里创建Student
类 - 注意:学生实体类必须实现
Writable
接口,才能作为Mapper
和Reducer
的输出值类型
package net.hpy.mr;
import org.apache.hadoop.io.Writable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
public class Student implements Writable {
private String name;
private double chinese;
private double math;
private double english;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getChinese() {
return chinese;
}
public void setChinese(double chinese) {
this.chinese = chinese;
}
public double getMath() {
return math;
}
public void setMath(double math) {
this.math = math;
}
public double getEnglish() {
return english;
}
public void setEnglish(double english) {
this.english = english;
}
public void write(DataOutput dataOutput) throws IOException {
dataOutput.writeUTF(name);
dataOutput.writeDouble(chinese);
dataOutput.writeDouble(math);
dataOutput.writeDouble(english);
}
public void readFields(DataInput dataInput) throws IOException {
name = dataInput.readUTF();
chinese = dataInput.readDouble();
math = dataInput.readDouble();
english = dataInput.readDouble();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", chinese=" + chinese +
", math=" + math +
", english=" + english +
'}';
}
}
5、创建科目平均分映射器类
- 在
net.hpy.mr
包里创建SubjectAvgMapper
类
- 由于MR程序读取
/subjectavg/input
目录里的三个科目成绩文件,在Mapper获取文件切片时就要区分读取的是哪一个文件,可以通过context的getInputSplit()方法获得文件切片对象,由此可以获取该切片对应的文件名,从而知道读取的是哪一科的成绩文件。
package net.hpy.mr;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import java.io.IOException;
public class SubjectAvgMapper extends Mapper<LongWritable, Text, Text, Student> {
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
// 获取文件切片对象(将输入切片强转成文件切片)
FileSplit split = (FileSplit) context.getInputSplit();
// 获取文件切片对应的文件名
String filename = split.getPath().getName();
// 拆分行获取成绩数据
String line = value.toString();
String[] data = line.split(" ");
String name = data[1];
int score = Integer.parseInt(data[2]);
// 创建学生实体
Student student = new Student();
// 设置姓名属性
student.setName(name);
// 根据读取文件名来设置相应科目成绩
if (filename.contains("chinese")) {
student.setChinese(score);
} else if (filename.contains("math")) {
student.setMath(score);
} else if (filename.contains("english")){
student.setEnglish(score);
}
context.write(new Text(name), student);
}
}
6、创建科目平均分归并器类
- 在
net.hpy.mr
包里创建SubjectAvgReducer
类
package net.hpy.mr;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
import java.text.DecimalFormat;
public class SubjectAvgReducer extends Reducer<Text, Student, Text, Student> {
@Override
protected void reduce(Text key, Iterable<Student> values, Context context) throws IOException, InterruptedException {
// 创建学生对象
Student student = new Student();
// 设置姓名属性
student.setName(key.toString());
// 遍历学生迭代器,累加各科成绩
for (Student value : values) {
student.setChinese(student.getChinese() + value.getChinese());
student.setMath(student.getMath() + value.getMath());
student.setEnglish(student.getEnglish() + value.getEnglish());
}
// 求各科平均分
DecimalFormat df = new DecimalFormat("##.#");
student.setChinese(Double.parseDouble(df.format(student.getChinese() / 12)));
student.setMath(Double.parseDouble(df.format(student.getMath() / 12)));
student.setEnglish(Double.parseDouble(df.format(student.getEnglish() / 12)));
// 输出处理后的键值对
context.write(key, student);
}
}
7、创建科目平均分驱动器类
- 在
net.hpy.mr
包里创建SubjectAvgDriver
类
package net.hpy.mr;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import java.net.URI;
/**
* 功能:成绩和驱动器类
* 作者:华卫
* 日期:2022年12月02日
*/
public class SubjectAvgDriver{
public static void main(String[] args) throws Exception {
// 创建配置对象
Configuration conf = new Configuration();
// 设置数据节点主机名属性
conf.set("dfs.client.use.datanode.hostname", "true");
// 获取作业实例
Job job = Job.getInstance(conf);
// 设置作业启动类
job.setJarByClass(SubjectAvgDriver.class);
// 设置Mapper类
job.setMapperClass(SubjectAvgMapper.class);
// 设置map任务输出键类型
job.setMapOutputKeyClass(Text.class);
// 设置map任务输出值类型
job.setMapOutputValueClass(Student.class);
// 设置Reducer类
job.setReducerClass(SubjectAvgReducer.class);
// 设置reduce任务输出键类型
job.setOutputKeyClass(Text.class);
// 设置reduce任务输出值类型
job.setOutputValueClass(Student.class);
// 定义uri字符串
String uri = "hdfs://master:9000";
// 创建输入目录
Path inputPath = new Path(uri + "/subjectavg/input");
// 创建输出目录
Path outputPath = new Path(uri + "/subjectavg/output");
// 获取文件系统
FileSystem fs = FileSystem.get(new URI(uri), conf);
// 删除输出目录(第二个参数设置是否递归)
fs.delete(outputPath, true);
// 给作业添加输入目录(允许多个)
FileInputFormat.addInputPath(job, inputPath);
// 给作业设置输出目录(只能一个)
FileOutputFormat.setOutputPath(job, outputPath);
// 等待作业完成
job.waitForCompletion(true);
// 输出统计结果
System.out.println("======统计结果======");
FileStatus[] fileStatuses = fs.listStatus(outputPath);
for (int i = 1; i < fileStatuses.length; i++) {
// 输出结果文件路径
System.out.println(fileStatuses[i].getPath());
// 获取文件系统数据字节输入流
FSDataInputStream in = fs.open(fileStatuses[i].getPath());
// 将结果文件显示在控制台
IOUtils.copyBytes(in, System.out, 4096, false);
}
}
}
8、启动应用,查看结果
-
运行
SubjectAvgDriver
类
-
下载结果文件 -
part-r-00000
-
查看结果文件 -
part-r-00000