简介:这份实验报告PDF面向大数据技术入门学习者,对应《大数据技术原理与应用》课程,系统整理了Linux基本操作与Hadoop平台使用的实验过程与结果分析。内容覆盖Linux发展历史、Shell常用命令与快捷键、用户及文件权限管理、目录结构与文件操作,以及Hadoop单机模式与伪分布式部署、配置文件修改、HDFS读写原理、NameNode与DataNode角色、MapReduce的Map与Reduce阶段工作机制等核心知识点,并记录了实验环境搭建中Java JDK配置、SSH免密登录、Hadoop编译等常见问题的排查思路。资源包共1个PDF文件,大小约1.92MB,结构完整、便于打印或对照复习。目前已有114人学习下载,适合正在完成课程实验、需要参考实验步骤与结果分析模板的高校学生,也可作为梳理Linux与Hadoop基础操作的复习材料。
1. 从一份实验报告说起:大数据技术栈到底该怎么上手
很多人第一次接触大数据,是从课程实验报告开始的。标题里这份《大数据技术与应用》微课视频版实验报告,背后对应的其实是一整套从零搭建的动手路径:Linux 环境准备、Hadoop 伪分布式搭建、HDFS 常用命令、MapReduce 编程实例,最后落到一个综合实训。它解决的不是"大数据是什么"这种概念问题,而是"我手上只有一台电脑,怎么把这一整套东西跑起来、跑通、跑出结果"。
适合谁看?在校学生做课程设计、毕业设计,转行的人想补一套能写进简历的实操经验,还有已经会写 SQL 但没碰过分布式存储和计算的人。这篇笔记按实验报告里最常见的推进顺序拆开:先把 Linux 和 Hadoop 环境立住,再讲 HDFS 的读写流程和命令,然后进 MapReduce 编程,最后给一套综合实训的落地思路和排错清单。每一步都给可复制的命令和参数说明,新手能跟着走,熟手能对照边界。
2. 环境先立住:Linux 准备与 Hadoop 伪分布式搭建
2.1 为什么实验环境几乎都选 Linux 加伪分布式
Hadoop 原生就跑在 Linux 上,Windows 上要么用 WSL,要么用 Docker 镜像,坑多且和教材对不上。伪分布式(Pseudo-Distributed)指的是所有守护进程——NameNode、DataNode、ResourceManager、NodeManager——都跑在同一台机器上,但走的是完整的分布式通信逻辑。它和单机模式(Standalone)的区别很关键:单机模式用的是本地文件系统,根本不碰 HDFS;伪分布式才会真正启动 HDFS 和 YARN,你写的 MapReduce 程序才会走完整的提交、调度、执行链路。
选它的理由很实际:一台 8G 内存的笔记本就能跑,实验报告里的所有环节——HDFS 命令、MapReduce 作业、甚至和 ZooKeeper 整合——都能覆盖。生产集群部署策略那是另一回事,实验阶段先把伪分布式吃透,理解清楚每个进程干什么,后面上真集群只是把进程分散到多台机器。
Linux 发行版选哪个?教材一般用 CentOS 或 Ubuntu。现在 CentOS 停更了,我一般建议用 Ubuntu 22.04 LTS 或者 Rocky Linux,命令体系和教材基本兼容。虚拟机安装 Linux 偶尔会遇到蓝屏,多半是虚拟化没在 BIOS 里打开,或者 Hyper-V 和 VMware 冲突,这个先排查掉。
2.2 从零安装 Hadoop 的关键步骤
先确认 Java 环境,Hadoop 3.x 需要 JDK 8 或 11:
# 检查 Java 版本,没有就装 java -version sudo apt update sudo apt install openjdk-8-jdk -y # 配置 JAVA_HOME,写进环境变量 echo 'export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64' >> ~/.bashrc echo 'export PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc source ~/.bashrc逻辑说明:Hadoop 的所有守护进程都是 Java 进程,JAVA_HOME 必须能被 Hadoop 启动脚本读到,否则启动时报 "JAVA_HOME is not set"。参数上,JDK 8 兼容性最好,JDK 11 在 Hadoop 3.3 之后也支持,但教材实验用 8 最稳。
下载并解压 Hadoop,配置 SSH 免密(伪分布式必须,否则启动脚本会反复要密码):
# 下载 Hadoop(版本按教材,这里以 3.3.6 为例) wget https://archive.apache.org/dist/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz tar -zxvf hadoop-3.3.6.tar.gz -C /usr/local/ mv /usr/local/hadoop-3.3.6 /usr/local/hadoop # 配置 SSH 免密登录本机 ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys ssh localhost # 测试,能免密进去就对了接下来是核心配置文件,一共要改五个,都在$HADOOP_HOME/etc/hadoop/下:
| 配置文件 | 关键参数 | 作用 |
|---|---|---|
| core-site.xml | fs.defaultFS=hdfs://localhost:9000 | 指定默认文件系统为 HDFS |
| core-site.xml | hadoop.tmp.dir=/usr/local/hadoop/tmp | 临时目录,必须手动建 |
| hdfs-site.xml | dfs.replication=1 | 伪分布式只有一份副本 |
| mapred-site.xml | mapreduce.framework.name=yarn | MapReduce 跑在 YARN 上 |
| yarn-site.xml | yarn.nodemanager.aux-services=mapreduce_shuffle | NodeManager 的辅助服务 |
# 建临时目录,忘了这步启动必失败 mkdir -p /usr/local/hadoop/tmp # 格式化 NameNode,只能执行一次 hdfs namenode -format # 启动 HDFS 和 YARN start-dfs.sh start-yarn.sh # 验证进程 jpsjps应该看到 NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager 五个进程。少一个就去看对应日志,日志在$HADOOP_HOME/logs/下。
提示:
hdfs namenode -format只能执行一次。重复格式化会导致 clusterID 不一致,DataNode 起不来,报 "Incompatible clusterIDs"。真格式化了,就把 tmp 目录清掉重新来。
2.3 参数怎么改、失败看什么
dfs.replication在伪分布式下必须设 1,设 3 会一直报副本不足的警告,因为只有一个 DataNode。hadoop.tmp.dir默认在 /tmp 下,重启机器可能被清掉,导致集群数据丢失,所以一定要改到持久目录。
启动失败最常见的三类:一是 JAVA_HOME 没配好,看hadoop-env.sh里有没有显式指定;二是 SSH 免密没配通,start-dfs.sh会卡在输入密码;三是端口占用,9000、8088、9870 这几个端口被别的服务占了。排查顺序就是先jps看进程,再翻 logs 目录下对应组件的 .log 文件,报错信息一般很直白。
3. HDFS 读写流程与常用命令:别只会 put 和 get
3.1 HDFS 写入数据的流程到底走了几步
理解写入流程,排错时才知道卡在哪。客户端调FileSystem.create()写文件时,背后是这样一条链路:
第一步,客户端向 NameNode 发起创建请求,NameNode 检查路径是否存在、权限够不够,通过后在命名空间里记一条新文件记录,但此时不分配任何数据块。第二步,客户端开始写数据,数据先被切成块(默认 128MB),每个块向 NameNode 申请一组 DataNode 位置,NameNode 按机架感知策略返回一个管道(pipeline),比如三副本就是 DN1→DN2→DN3。第三步,客户端把数据包发给 DN1,DN1 收到后转发给 DN2,DN2 再转发给 DN3,形成流水线。第四步,每个 DN 写完落盘后逐级回传 ack,全部成功客户端才继续写下一个包。第五步,文件写完客户端调close(),NameNode 才把元数据落盘。
读流程相对简单:客户端向 NameNode 请求文件块位置,NameNode 返回按网络距离排序的 DataNode 列表,客户端直接连最近的 DataNode 读数据,读完一个块再读下一个。NameNode 不参与实际数据传输,这也是 HDFS 能扛高吞吐的原因。
3.2 hdfs 常用命令与基本操作
命令行是实验报告里出现频率最高的部分,这些命令必须练到不用查:
# 查看 HDFS 根目录 hdfs dfs -ls / # 创建目录 hdfs dfs -mkdir -p /user/student/input # 上传本地文件到 HDFS hdfs dfs -put localfile.txt /user/student/input/ # 从 HDFS 下载到本地 hdfs dfs -get /user/student/input/localfile.txt ./ # 查看文件内容 hdfs dfs -cat /user/student/input/localfile.txt # 查看文件大小和副本信息 hdfs dfs -ls -h /user/student/input/ # 删除文件(-r 删目录) hdfs dfs -rm -r /user/student/input/localfile.txt # 查看磁盘使用情况 hdfs dfs -df -h参数说明:-put和-copyFromLocal等价,-get和-copyToLocal等价,教材里两种写法都出现过。-ls -h的-h是把字节数转成人类可读的 KB/MB/GB。-rm -r删目录必须带-r,否则报错。
还有一个容易被忽略的命令hdfs fsck,用来检查文件系统健康度:
# 检查整个文件系统 hdfs fsck / # 检查指定文件,列出块和副本位置 hdfs fsck /user/student/input/localfile.txt -files -blocks -locationsfsck会报缺失块、副本不足、块损坏等问题。这里要提醒一句:hdfs fsck这类管理接口如果暴露在公网且没做权限控制,是存在未授权访问风险的,实验环境无所谓,但任何对外提供服务的集群都必须配好 Kerberos 或至少限制访问来源,这是运维底线。
3.3 读写流程里最容易翻车的两个点
第一个是小文件问题。HDFS 每个文件、每个块在 NameNode 内存里都占约 150 字节元数据,上传几万个几 KB 的小文件,NameNode 内存会被吃光。实验里数据量小感觉不到,真上生产就是灾难。常见做法是先合并再上传,或者用 HAR 归档。
第二个是副本因子和块大小的配合。块大小默认 128MB,改大改小要看场景:块大,寻址开销小但单块失败重传代价高;块小,并行度高但 NameNode 压力大。伪分布式下副本设 1,别照抄生产的三副本配置,否则一直报副本不足。
4. MapReduce 编程实例:从 WordCount 到自定义排序
4.1 MapReduce 的执行模型和编程套路
MapReduce 把计算拆成 Map、Shuffle、Reduce 三个阶段。Map 阶段读输入分片,每行调一次 map 函数,输出键值对;Shuffle 阶段把相同 key 的数据拉到同一个 Reduce 节点,中间包含分区、排序、合并;Reduce 阶段对每个 key 的 value 列表做聚合。
写一个 MapReduce 程序,套路固定:Mapper 类继承Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT>,重写map();Reducer 类继承Reducer<KEYIN, VALUEIN, KEYOUT, VALUEOUT>,重写reduce();Driver 里配 Job 的输入输出路径、Mapper、Reducer 类、输出键值类型。
4.2 WordCount 完整代码与运行
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; import java.io.IOException; import java.util.StringTokenizer; public class WordCount { // Mapper:每行文本切词,输出 <单词, 1> 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); } } } // 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); } } 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(TokenizerMapper.class); job.setCombinerClass(IntSumReducer.class); // Combiner 减少网络传输 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); } }逻辑说明:Mapper 里StringTokenizer按空白切词,每个词输出<word, 1>。Reducer 把相同 word 的 1 累加。setCombinerClass是关键优化——Combiner 在 Map 端先做一次局部聚合,把<word, 1>合并成<word, n>再发给 Reducer,能大幅减少 Shuffle 阶段的网络传输。Combiner 逻辑和 Reducer 一样时可以直接复用,但前提是操作满足结合律和交换律,求和、求最大值可以,求平均值不行。
编译打包运行:
# 编译 javac -classpath $(hadoop classpath) -d classes WordCount.java # 打包 jar -cvf wordcount.jar -C classes/ . # 准备输入数据并上传 hdfs dfs -mkdir -p /user/student/wordcount/input echo "hello world hello hadoop" > input.txt hdfs dfs -put input.txt /user/student/wordcount/input/ # 运行 hadoop jar wordcount.jar WordCount \ /user/student/wordcount/input \ /user/student/wordcount/output # 查看结果 hdfs dfs -cat /user/student/wordcount/output/part-r-00000参数说明:hadoop classpath会输出所有依赖 jar 路径,编译时必须带上。输出目录必须不存在,否则 Job 直接报FileAlreadyExistsException,这是新手最常踩的坑。
4.3 自定义排序与分组排序怎么做
MapReduce 默认按 key 的自然顺序排序。要做自定义排序,实现WritableComparable接口,重写compareTo()。比如按温度降序排:
public class TempKey implements WritableComparable<TempKey> { private int year; private int temp; @Override public int compareTo(TempKey o) { // 先按年份升序,同年按温度降序 if (this.year != o.year) { return Integer.compare(this.year, o.year); } return Integer.compare(o.temp, this.temp); } @Override public void write(DataOutput out) throws IOException { out.writeInt(year); out.writeInt(temp); } @Override public void readFields(DataInput in) throws IOException { year = in.readInt(); temp = in.readInt(); } // getter/setter 省略 }分组排序(二次排序)要额外写一个GroupingComparator,让相同年份但不同温度的记录进同一个 reduce 调用:
public class YearGroupingComparator extends WritableComparator { protected YearGroupingComparator() { super(TempKey.class, true); } @Override public int compare(WritableComparable a, WritableComparable b) { TempKey k1 = (TempKey) a; TempKey k2 = (TempKey) b; return Integer.compare(k1.getYear(), k2.getYear()); } }Driver 里设置job.setGroupingComparatorClass(YearGroupingComparator.class)。这样排序按"年份升序+温度降序",但分组只按年份,reduce 里拿到的 value 列表就是同一年按温度排好序的。这个模式在"求每年最高温度""求每组 TopN"这类需求里非常常用。
注意:自定义 key 必须实现
WritableComparable,否则序列化会失败。compareTo的返回值决定排序,返回 0 表示相等,会影响分组,写的时候要清楚自己在控制排序还是分组。
5. 避坑与排查:实验里最常翻车的五个地方
5.1 格式化后 DataNode 起不来
现象:jps里只有 NameNode,没有 DataNode,日志报 "Incompatible clusterIDs"。
原因:重复执行了hdfs namenode -format,NameNode 的 clusterID 变了,DataNode 里存的还是旧的。
解决:停掉集群,删掉hadoop.tmp.dir下所有内容,重新格式化一次,再启动。记住格式化只做一次。
5.2 输出目录已存在导致 Job 失败
现象:运行 MapReduce 报org.apache.hadoop.mapred.FileAlreadyExistsException。
原因:FileOutputFormat要求输出路径不存在,防止覆盖已有结果。
解决:每次运行前删掉输出目录hdfs dfs -rm -r /output,或者代码里在 Driver 加判断自动删除。别图省事直接改源码绕过,这是保护机制。
5.3 内存不足导致容器被杀
现象:YARN 报 "Container killed on request. Exit code is 143",或者 "beyond physical memory limits"。
原因:伪分布式默认给每个容器分配的内存和机器实际内存不匹配,或者数据倾斜导致某个 reduce 处理的数据量远超预期。
解决:调yarn-site.xml里的yarn.nodemanager.resource.memory-mb和mapreduce.map.memory.mb、mapreduce.reduce.memory.mb,按机器实际内存合理分配。数据倾斜的话,检查 key 分布,必要时加随机前缀打散。
5.4 中文乱码
现象:MapReduce 处理含中文的文本,输出乱码。
原因:输入文件编码和 Hadoop 默认读取编码不一致,或者Text类型按 UTF-8 处理但源文件是 GBK。
解决:统一用 UTF-8 保存输入文件,Linux 下用file -i确认编码,必要时iconv转换。别在代码里硬编码编码,容易顾此失彼。
5.5 端口被占用启动失败
现象:start-dfs.sh后进程起不来,日志报 "Address already in use"。
原因:9000、9870、8088 等端口被其他进程占用,或者上次集群没停干净。
解决:netstat -tlnp | grep 端口号找到占用进程,要么杀掉,要么改 Hadoop 对应配置文件的端口。停集群用stop-dfs.sh和stop-yarn.sh,别直接 kill -9,容易留下 pid 文件导致下次启动异常。
6. 综合实训怎么落地:从招聘数据清洗到可视化
综合实训是实验报告里分值最高的部分,常见题目是"招聘数据清洗"或"交通信息分析"。这类题目的通用套路是:原始数据(CSV/JSON)→ 上传 HDFS → MapReduce 清洗(去重、去空、字段规整、格式统一)→ 输出结构化结果 → 导入 Hive 做 SQL 分析 → 可视化。
清洗阶段的 Map 只做过滤和规整,不改变数据粒度;Reduce 做去重时用 key 的唯一性天然去重。比如招聘数据清洗,把"公司+职位+薪资"拼成 key,value 留整条记录,Reduce 里只输出第一条,就完成了去重。字段规整用正则提取薪资区间、把"面议"统一成特定标记,这些逻辑写在 map 里。
验证清洗效果,别只看输出条数。我一般会做三件事:一是抽样对比,随机抽 100 条原始记录和清洗后记录人工核对;二是统计字段空值率,清洗前后各算一遍;三是用hdfs fsck确认输出文件块完整。最后把结果导到 Hive 建外部表,跑几个聚合 SQL 验证数据可用性。
有个习惯我保持了几年:每做完一个实训,把踩过的坑和对应的报错原文记到一个 markdown 里,下次遇到直接搜。大数据这套东西,命令和配置记不住很正常,但排错的思路和日志位置记熟了,什么问题都能顺着摸下去。希望帮到你。
本文还有配套的精品资源,点击获取