
简介面向大数据课程学习者的实验报告完整版适合正在学习Linux基础操作与Hadoop入门的学生对照练习。内容涵盖实验一“熟悉常用的Linux操作和Hadoop操作”的完整流程从安装Ubuntu 16.04虚拟机到配置Hadoop 2.7.1伪分布式环境均有详细记录并逐条演示了cd、ls、mkdir、cp、mv、rm、cat、tar、find等常用命令的具体用法可帮助初学者快速搭建大数据实验环境。资源包为单个docx文档共1个文件总大小3.29MB文字内容为主结构清晰适合直接阅读和按步骤复现实验。该资源已有5244人学习下载兼具课程作业参考与自学自测价值。除命令演示外报告还补充了环境变量配置、NameNode格式化、启动HDFS守护进程等关键细节读者可借此排查常见安装问题并加深对Hadoop运行机制的理解。1. 课程实验报告完整版到底是什么样的活从集群启动到指标闭环一份《大数据原理与技术课程实验报告完整版》如果你只是把它当成“填空题”——把实验步骤截图、运行结果粘贴、最后写一段心得——那大概率会在答辩环节被问穿。因为这份报告真正被检验的不是你有没有跑通命令而是你有没有理解数据从采集、存储到计算分析的全链路逻辑。我见过太多报告Hadoop 集群起来了、WordCount 也出了结果但一问“你的数据到底存在哪个目录、作业跑完有几个 Map 几个 Reduce、失败的任务怎么排查”就答不上来。所以这篇笔记的定位很直接把一份能拿得出手的大数据原理课程实验报告拆成可以照做的完整方案。内容包括环境选型与部署、数据采集与落盘、计算与分析三大主线再加上避坑清单和验收技巧。适合正在做课程设计的学生、刚入行的数据工程师以及需要把实验报告改造成面试作品的人。全文不依赖任何特定学校模板你只需要照着做然后把你自己的实验截图和参数填进报告。2. 环境选型与集群部署先定三节点拓扑再谈配置参数2.1 为什么我不建议你在单机虚拟机上硬扛课程实验报告里最常见的技术选型有两种一种是在一台 16GB 内存的笔记本上用 VMware 开三台虚拟机每台分 2GB 内存另一种是用 Docker Compose 在物理机上直接拉起一个三节点集群。我在帮学生改报告时强烈推荐后者原因有三。第一三台虚拟机各自跑一套完整 Linux 系统光系统本身就要占掉 1.5GB 到 2GB 内存留给 Hadoop 的堆内存所剩无几。而 Docker 容器共享宿主机内核单容器内存开销远低于虚拟机在 16GB 内存的电脑上就能跑起 NameNode、DataNode、ResourceManager、NodeManager 四个核心进程。第二虚拟机快照虽然能救命但 Docker Compose 的启动和销毁速度快太多实验做完一条命令清理干净不会在硬盘上留下一堆残留文件。第三学校机房或实验室电脑环境各异Docker 的鞋带打包特性可以让你在本地调好的镜像原样搬到任何一台机器上跑这本身就是报告里值得写的“可复现性”亮点。但你要注意Docker 不是万能的。如果课程明确要求你“手工配置 Hadoop 集群”那 Docker 就只能在附录里作为补充方案主线实验还是得老老实实用标准安装包搭。但现实是大多数课程实验报告只看最终结果和截图用 Docker 做了三节点集群部署反而更快、更不占电脑资源。2.2 一份可直接复制的 Docker Compose 配置三节点 Hadoop 集群我常用的配置是三节点一个 NameNode ResourceManager 主节点两个 DataNode NodeManager 从节点。这个拓扑虽然比生产环境小但已经覆盖了 HDFS 和 YARN 两个核心组件的完整职责划分写进报告已经足够了。下面这份 docker-compose.yml 是可直接照抄的镜像用的是 Hadoop 3.3.6 官方镜像有点玄学的是不同版本的 Hadoop 镜像踩的坑完全不同3.x 系列比 2.x 好用太多了。version: 3.8 services: namenode: image: bde2020/hadoop-namenode:2.0.0-hadoop3.2.1 container_name: namenode environment: - CLUSTER_NAMEtest-cluster ports: - 9870:9870 - 9000:9000 volumes: - namenode_data:/hadoop/dfs/name - ./data/input:/input networks: - hadoop-net datanode1: image: bde2020/hadoop-datanode:2.0.0-hadoop3.2.1 container_name: datanode1 environment: - CLUSTER_NAMEtest-cluster ports: - 9864:9864 volumes: - datanode1_data:/hadoop/dfs/data networks: - hadoop-net datanode2: image: bde2020/hadoop-datanode:2.0.0-hadoop3.2.1 container_name: datanode2 environment: - CLUSTER_NAMEtest-cluster ports: - 9865:9864 volumes: - datanode2_data:/hadoop/dfs/data networks: - hadoop-net resourcemanager: image: bde2020/hadoop-resourcemanager:2.0.0-hadoop3.2.1 container_name: resourcemanager ports: - 8088:8088 networks: - hadoop-net nodemanager1: image: bde2020/hadoop-nodemanager:2.0.0-hadoop3.2.1 container_name: nodemanager1 environment: - CLUSTER_NAMEtest-cluster networks: - hadoop-net nodemanager2: image: bde2020/hadoop-nodemanager:2.0.0-hadoop3.2.1 container_name: nodemanager2 environment: - CLUSTER_NAMEtest-cluster networks: - hadoop-net volumes: namenode_data: datanode1_data: datanode2_data: networks: hadoop-net: driver: bridge这份配置里我特意把 Namenode 的 9000 端口映射了出来因为后面 Flume 写数据到 HDFS 时要用这个端口。9870 是 HDFS Web UI8088 是 YARN 的 ResourceManager Web UI。9864 和 9865 分别是两个 DataNode 的 HTTP 端口用来查看单个节点的存储状态。环境变量CLUSTER_NAME的值可以随便改但三个服务的集群名必须一致否则 DataNode 注册不上 NameNode。这是个经典坑镜像内部会按这个值初始化 HDFS 目录不一致时 NameNode 会拒绝 DataNode 注册表现为 datanode 日志里反复出现Block report失败。启动命令就一行docker-compose up -d启动后先不要急着跑作业花三十秒做三件事判断集群是否健康。第一docker-compose ps看所有容器状态是否为 Up第二浏览器打开http://localhost:9870确认 NameNode 页面进入了 Active 状态第三在 namenode 容器里执行hdfs dfsadmin -report看 Live datanodes 是不是两个。这三步都过了才算集群部署成功。2.3 报告里需要你补的配置参数core-site.xml、hdfs-site.xml、yarn-site.xmlDocker 镜像已经把多数基础配置帮你写好了但课程实验报告一般要求附上关键配置文件。我建议你把下面三个文件的核心参数原样抄进报告并解释含义因为答辩时老师大概率会围着这些参数提问。core-site.xml 里最核心的是fs.defaultFS它把默认文件系统指向 HDFS值是hdfs://namenode:9000。注意这里用的是容器名namenode而不是 localhost因为容器间通信靠 Docker 网络中的主机名解析。hdfs-site.xml 里要关注dfs.replication默认值是 2。我们的集群只有两个 DataNode所以副本数设 2 是合理的。如果设成 3写入时会报NotReplicatedYetException或一直处于 under-replicated 状态。这个参数在 HDFS 页面里可以直接看到也是个很直观的答辩问题。yarn-site.xml 里最关键的是yarn.nodemanager.resource.memory-mb和yarn.scheduler.maximum-allocation-mb。Docker 默认容器不限制内存的话NodeManager 会按宿主机物理内存的一半来申请如果笔记本只有 16GB 就可能出问题。一般我会在 nodemanager 容器里显式加环境变量YARN_NODEMANAGER_OPTS-Xmx1024m并把下面的参数配好property nameyarn.nodemanager.resource.memory-mb/name value4096/value /property property nameyarn.scheduler.maximum-allocation-mb/name value2048/value /property这两个参数决定了 YARN 能分配给每个容器container的最大内存。如果yarn.scheduler.maximum-allocation-mb设得太低后面跑 Spark 作业时会直接报Container exited with a non-zero exit code 143这是被 Linux OOM Killer 杀的典型信号。2.4 集群部署这一章的验收标准写完这一章你需要明确告诉读者什么状态算“集群部署成功”不是看到几个容器跑起来就完事。我的验收标准是三条第一HDFS 页面能看到 2 个活的 DataNode存储容量加起来大于 100GB第二YARN 页面能看到 2 个活的 NodeManager可用内存大于 4GB第三在 namenode 容器里执行hdfs dfs -mkdir /test然后hdfs dfs -put 任意文件 /test能成功上传和下载。如果你连这一步都过不了不要急着进下一章先把集群状态调好。因为后面的 Flume 采集和 MapReduce 计算全部依赖这套基础环境基础不牢地动山摇。3. 数据采集与落盘用 Flume 把模拟日志从零写入 HDFS3.1 实验报告的常见缺口只有计算没有采集很多课程实验报告的流程是老师给一个数据集直接hdfs dfs -put到 HDFS 上然后用 WordCount 跑一遍。这样做实验报告不难看但你仔细想这是不是把大数据链路里最引以为傲的“采集”环节给省了真正完整的实验报告应该覆盖全链路而数据采集正是大多数学生的盲区。Flume 是一个分布式的日志收集系统常用来把服务器上的日志实时采集到 HDFS。在实验报告里加一个 Flume 章节不仅显得你懂采集还能把流式数据的概念带出来。关键是它配置起来并不难一条 conf 文件加一条启动命令就能跑通。3.2 最小 Flume 配置监听目录自动上传 HDFS课程实验里最常见的场景是让 Flume 监听一个目录只要这个目录下出现新的日志文件就自动抓取并写入 HDFS。这个场景够简单、效果够直观也很适合截图写报告。# flume-kafka-hdfs.conf agent.sources fileSrc agent.channels fileChannel agent.sinks hdfsSink # source: 监听 /home/data/logs 目录下新增文件 agent.sources.fileSrc.type spooldir agent.sources.fileSrc.spoolDir /home/data/logs agent.sources.fileSrc.fileHeader true agent.sources.fileSrc.deletePolicy never # 这里如果用 kafka 做中转可以在 source 和 sink 之间插入一个 kafka channel # channel: 使用 file channel落本地磁盘防止数据丢失 agent.channels.fileChannel.type file agent.channels.fileChannel.dataDirs /home/data/flume-channel agent.channels.fileChannel.checkpointDir /home/data/flume-checkpoint agent.channels.fileChannel.capacity 100000 # sink: 写入 HDFS agent.sinks.hdfsSink.type hdfs agent.sinks.hdfsSink.hdfs.path hdfs://namenode:9000/flume/events/%Y%m%d agent.sinks.hdfsSink.hdfs.fileType DataStream agent.sinks.hdfsSink.hdfs.writeFormat Text agent.sinks.hdfsSink.hdfs.rollInterval 60 agent.sinks.hdfsSink.hdfs.rollSize 1024 agent.sinks.hdfsSink.hdfs.rollCount 0 agent.sinks.hdfsSink.hdfs.batchSize 100 # 组装 agent.sources.fileSrc.channels fileChannel agent.sinks.hdfsSink.channel fileChannel解释几个关键参数。spooldir类型的 source 适合做离线批量实验它会把目录下出现的新文件按原名读走读完以后标记为.COMPLETED避免重复消费。deletePolicy设为never文件不会删方便你在报告里留证据——每次截图时都能看到源文件还在。hdfs.path里的%Y%m%d是自动按日期分目录这是 HDFS 上管理日志的常见做法也方便后续分析时按天做数据分区。rollInterval 60表示每 60 秒强制关闭当前 HDFS 文件滚动到下一个文件。如果把这个值设成 0那 Flume 会一直写同一个文件直到文件被填满这样你很难在 HDFS 页面看到持续生成的增量文件。rollSize是文件大小阈值1024 字节或更大实验里我不会设太大因为小文件滚动方便你在浏览器里刷新看到多个.tmp文件出现。启动 Flume 的命令单独写一个 nohup 脚本nohup flume-ng agent \ --conf /home/data/flume-conf \ --conf-file /home/data/flume-conf/flume-kafka-hdfs.conf \ --name agent \ -Dflume.root.loggerINFO,console \ /home/data/flume.log 21 这个命令挂在后台跑日志输出到 flume.log。第一次启动时先不要加nohup用前台模式跑几秒确认日志里没有Configuration Error再转后台。3.3 模拟数据源用脚本定时生成带格式日志Flume 监听目录不会凭空出现文件你还得造数据。我在实验报告里一般会附一个生成日志的脚本用 Python 定时生成带用户 ID、行为类型和时间戳的模拟日志。# gen_logs.py import time import random import os # 模拟一批电商用户行为日志 actions [view, click, add_cart, purchase] output_dir /home/data/logs os.makedirs(output_dir, exist_okTrue) while True: # 每个文件写 1000 行日志 fname time.strftime(%Y%m%d%H%M%S) _ str(random.randint(100, 999)) .log fpath os.path.join(output_dir, fname) with open(fpath, w) as fp: for i in range(1000): ts time.strftime(%Y-%m-%d %H:%M:%S) user_id random.randint(10001, 99999) action random.choice(actions) amount 0 if action purchase: amount round(random.uniform(10, 999), 2) # 将日志写为 CSV 风格时间,用户ID,行为,金额 fp.write(f{ts},{user_id},{action},{amount}\n) print(fgenerated {fpath}) time.sleep(10)这个脚本每 10 秒生成一个 1000 行的日志文件。Flume 的 spooldir source 会捕获新文件并启动一条事务把它写入 HDFS。注意这个脚本必须和 Flume 跑在同一台机器上否则监听目录在不同机器上路径就要改成 NFS 或共享目录那就复杂了。实验里我把日志格式特意设成 CSV 风格是为了后面用 Hive 或 Spark SQL 直接查询时能顺利映射成结构化表。如果你的报告后面要写 Hive 表这个设计就是加分项。3.4 这一章的验证数据是真的落到了 HDFS跑起来以后不要急着写报告。先在浏览器打开http://localhost:9870在 Utilities 菜单里点 “Browse the file system”切到/flume/events/日期目录你会看到正在写的.tmp文件点赞刷新几次文件会一个个出现并变成正式文件。这比任何截图都能证明 Flume 采集链路是通的。还可以用命令行验证hdfs dfs -ls /flume/events/$(date %Y%m%d) hdfs dfs -tail /flume/events/$(date %Y%m%d)/某个文件名第二条命令直接看 HDFS 文件尾部内容能看到新写入的日志行。如果日志内容是乱码检查的是hdfs.fileType别用SequenceFile一定要用DataStream。3.5 实验报告里数据量级的小技巧写报告时有一处细节很多人会忽略你的实验数据集要能支撑后面的计算分析。Flume 才采集了 1000 行日志纯手写数据集也只有几万行这对 MapReduce 来说连热身都不够。我一般在报告里会额外用脚本生成一个约 200MB 到 500MB 的文本数据集随机生成 1000 万行以上的数据这样跑 WordCount 或 TopN 分析时Map 数量能上到几十个Reduce 也能跑出几个截图效果和数据量完全不一样。这个做法不是画蛇添足而是让阅卷老师看到你理解大数据的基本常识数据量不够大MapReduce 的优势根本体现不出来。大数据课程实验报告完整版指的不仅流程完整更是数据量级也要说得过去。4. 计算与分析MapReduce 作业的参数调优与结果验证4.1 WordCount 不是终点报告里要有你自己的分析逻辑如果实验报告里只有 WordCount那说实话技术含量偏低。我见过的大部分高分报告都会把 WordCount 当成基础入门紧接着加一个业务向的分析任务——比如日志里哪些用户行为最多、购买金额最高的 Top 10 用户是谁、按小时分析访问趋势。这个分析不会增加太多工作量但完全能决定报告的层次。完整报告的普遍写法是先用hadoop-mapreduce-examples自带的 WordCount 跑通流程再做自定义 MapReduce 作业。下面我给出第二种的模板代码包含 Mapper、Reducer 和 Driver 三部分目标是统计日志中每种行为的数量顺便算出购买总金额。4.2 自定义 MapReduce统计行为类型与购买总额// BehaviorCount.java import java.io.IOException; 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; public class BehaviorCount { // Mapper解析每行 CSV输出 行为类型 - 1 public static class BehaviorMapper extends MapperObject, Text, Text, IntWritable { private final static IntWritable one new IntWritable(1); private Text behavior new Text(); public void map(Object key, Text value, Context context) throws IOException, InterruptedException { // 每行格式时间,用户ID,行为,金额 String[] fields value.toString().split(,); if (fields.length 4) { behavior.set(fields[2]); context.write(behavior, one); } } } // Reducer累加结果 public static class BehaviorReducer extends ReducerText, IntWritable, Text, IntWritable { private IntWritable result new IntWritable(); public void reduce(Text key, IterableIntWritable 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, behavior count); job.setJarByClass(BehaviorCount.class); job.setMapperClass(BehaviorMapper.class); job.setCombinerClass(BehaviorReducer.class); job.setReducerClass(BehaviorReducer.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 按逗号切分每行日志取出第三个字段行为类型作为 key数值固定输出 1。Reducer 对每个行为类型累加得到最终的计数。我在代码里加了 Combiner它可以在 Map 阶段先本地合并一次减少网络传输数据量。对小数据集看不出差别但换到 GB 级数据时这个优化能让作业耗时减少最多 30%值得写进报告的“优化手段”里。编译和提交命令是标准的 Maven 流程提交前要确认输入路径和输出路径没有重名文件。输出路径如果已经存在MapReduce 会直接报错这是新手最容易翻车的一条。mvn clean package -DskipTests hadoop jar target/behavior-count-1.0.jar BehaviorCount /flume/events /output/behavior_count/跑完以后用以下命令验证输出结果hdfs dfs -cat /output/behavior_count/part-r-00000你会看到类似view 1234,purchase 567这样的结果。如果part-r-00000里有乱码或小方块那不是数据问题是终端编码不支持个别字符用hdfs dfs -text替代-cat即可。4.3 作业参数的必调项从 1 个 Reducer 到并行度调整Java 代码跑通了但在真实数据上你还要调并行度。默认情况下Reducer 的数量是 1因为代码里没有显示设置。日志数据量不大时1 个 Reducer 可以接受但你要是想把报告吹得硬一点就得在 Driver 里加下面几行设置// 根据输入数据大小估算 reducer 数量字节数 / 64MB 向上取整 long inputSize FileInputFormat.getInputPaths(job)[0].getFileSystem(conf) .getContentSummary(FileInputFormat.getInputPaths(job)[0]).getLength(); int numReducers (int) Math.ceil(inputSize / (64 * 1024 * 1024)); job.setNumReduceTasks(Math.max(1, numReducers));这段代码的意思是每 64MB 数据分配一个 Reducer。这个估算公式是常见做法既不会因为 Reducer 太少导致数据倾斜也不会因为太多而增加调度开销。如果你在实验数据量只有几 MB那 1 个 Reducer 就够强行设多反而会看到大部分 Reducer 输出为空报告截图更难看。Map 端的并行度由输入分片split数量决定非压缩文本每 128MB 一个分片在 Hadoop 3.x 里默认值YARN 上可配。小文件特别多时Map 数量会被抬高到一个不合理的量级每个 Map 只处理几 KB大量时间浪费在进程启停上。所以实验数据一定要尽量合并成大文件这也是我在前一章强调别一直生成小文件的原因。4.4 进阶对比Spark 版本如何写报告里才有含金量如果你的课程实验报告允许使用 Spark强烈建议把同一份分析用 Spark 再实现一遍。这不仅是展示技能更重要的是对比两者的性能与代码量差距让报告的分析部分更厚实。// BehaviorCount.scala import org.apache.spark.sql.SparkSession object BehaviorCount { def main(args: Array[String]): Unit { val spark SparkSession.builder() .appName(BehaviorCount) .getOrCreate() import spark.implicits._ // 读取 HDFS 上的 CSV 数据 val df spark.read .option(header, false) .csv(hdfs://namenode:9000/flume/events/) .toDF(time, user_id, behavior, amount) // 按行为分组计数 val result df.groupBy(behavior).count() result.show() // 购买总额 Top 5 用户 val topBuyers df.filter($behavior purchase) .groupBy(user_id) .sum(amount) .orderBy($sum(amount).desc) .limit(5) topBuyers.show() spark.stop() } }这段代码用 DataFrame API 实现了两个分析行为分布统计和购买金额 Top 5 用户。提交命令比 MapReduce 简单用spark-submit指定 master 为 yarnspark-submit \ --class BehaviorCount \ --master yarn \ --deploy-mode client \ --num-executors 2 \ --executor-memory 1g \ --executor-cores 2 \ target/behavior-count_2.12-1.0.jar提交以后打开http://localhost:8088你会看到 application 出现在 YARN 页面上点进去能看到 Executor 个数、任务数量和 Shuffle 字节数。这些指标截图放到报告里比只会贴控制台输出要说服力强得多。5. 实验报告最常见的 5 个坑从端口冲突到小文件雪崩5.1 救命就靠这一章现象、原因、解决全记录这一章是整份报告里最容易让评审老师觉得“你确实是亲手做的”的地方。因为正常的教程不会告诉你坑在哪只有真正做过的人才写得出来。我把这几年帮人排障看到的共性问题按“现象 → 原因 → 解决”的格式整理出来你可以直接抄进报告的“实验总结”部分也可以作为自己实验室内的排查手册。5.2 坑一DataNode 起不来页面只剩一个 Live Node现象是 HDFS Web UI 只显示一个 DataNode另一个怎么都不亮。看日志发现Block pool ID和NameNode的不一致或者报Storage directory already exists but it is incompatible。原因非常典型先启动了 NameNode 初始化了集群然后又重新格式化了 NameNode 或者换过容器卷DataNode 里保存的 cluster ID 已经变了。Docker 环境里最容易触发因为你docker-compose down后旧的 DataNode 数据卷还在新启动的 NameNode 重新生成了 cluster ID两者对不上。解决方法是把 NameNode 的元数据目录和所有 DataNode 的数据目录全部清掉重来。Docker 下执行docker-compose down -v把数据卷一起删掉再重新启动。如果你不想删数据卷也可以在 datanode 容器里执行hdfs datanode -format但注意这会清空该 DataNode 上的数据。哪个更省事取决于你对数据的态度。5.3 坑二端口冲突9870 被占用打不开现象是浏览器访问 9870 或 8088 端口一直转圈或拒绝连接但在容器内部访问一切正常。原因很简单你本机或宿主机上已经有别的进程占用端口。课程实验中很常见的是 Node.js 开发服务器占 8088或者另一套老版本的 HDFS 占用了 9870。Docker 映射端口时不会报错但流量进不到容器内。解决方法是改映射端口把9870:9870改成19870:9870访问时用http://localhost:19870。这样做不影响容器内部通信但报告里的截图地址会变注意截图时统一。5.4 坑三MapReduce 作业卡死容器反复被杀现象是作业提交后一直显示ACCEPTED或RUNNING但点进去看到 Container 大量反复启动和退出日志里有Killed by OOM-Killer。原因是 NodeManager 容器内存不足。YARN 会按照yarn.nodemanager.resource.memory-mb给 NodeManager 分配总可用的内存然后yarn.scheduler.maximum-allocation-mb控制单个 Container 的最大内存。如果 MapReduce 的mapreduce.map.memory.mb默认是 1024MB 但你的maximum-allocation-mb配的比 1024MB 小那每个 Container 根本分配不到足够的物理内存一启动就被系统杀死。解决方式是回去改 yarn-site.xml把yarn.nodemanager.resource.memory-mb设为你实际能接受的值yarn.scheduler.maximum-allocation-mb至少 2048MB。然后docker-compose restart nodemanager1 nodemanager2让配置生效。5.5 坑四HDFS 上小文件爆炸NameNode 内存不够用现象是跑完 Flume 采集后HDFS 里一个目录下有几百个 1KB 的小文件NameNode 页面显示已用存储空间不多但 NameNode 堆内存告急后续写入开始卡顿。原因是 Flume 默认每 60 秒滚动一个文件你的日志生成脚本每 10 秒一个文件加上 MapReduce 的每个输出分区也是一个文件小文件数量是指数级增长的。NameNode 把每个文件、每个目录、每个块的信息都放内存小文件多就是内存杀手。解决方式有两个方向。第一个是从源头合并Flume 的hdfs.rollInterval设成 0hdfs.rollCount设成 0按大小滚动hdfs.rollSize设成 128MB 左右让它尽量写大文件。第二个是事后合并跑一个hadoop distcp -update -append到新目录或者用 Spark 的coalesce(1)输出合并文件。写报告时你要体现你理解这两条路线。5.6 坑五数据倾斜导致整个 MapReduce 看起来“卡死”了现象是所有 Map 任务早就跑完了但有一个或两个 Reduce 任务从 33% 开始长时间不动最后成绩惨淡。原因是数据分布不均匀比如你按行为类型分组view事件数量是purchase的几百倍那一个 Reduce 要处理几百倍的数据量。日志里看某个 reducer 的 shuffle bytes 明显比其他 reducer 大几个数量级。解决方法是调job.setNumReduceTasks或者引入自定义分区器但实验中最简单的做法是改用 Spark SQL 分析它会自动做广播变量和统计优化。另一个常见做法是把倾斜 key 加上随机盐再分桶这是散落分布老经验。报告里写清楚你观察到了这个现象并说明你尝试的解决方案哪怕没完全解决也比装看不见诚实。6. 把实验报告改造成面试作品扩展方向、验证方法与数据管道闭环说到底实验报告完整版的价值不在那张课程表而在于你能不能把这套流程迁移到真实场景去讲。我在最后给大家三条具体建议都是我自己改报告和面试新人时最看重的点。第一报告里至少要有一张全链路架构图和数据流向图。虽然这篇文章不用 mermaid但你在装订报告里画一张简单的流程图很加分。画法是数据源 → Flume → HDFS → 离线分析MapReduce/Spark → 结果导出 → 可视化展示。旁边标上每一段的技术选型和数据格式评委第一眼就能看出你理解的是整套大数据管道不是一个词一个命令。第二验证结果不要只贴控制台。我一般会要求报告里至少包含三类证据HDFS 页面截图证明数据真实落盘YARN 页面截图证明作业真实执行输出结果文件截图证明分析结论真实产出。三者缺一不可。如果你能把 DateNode 里某个文件内容和源文件做比对那就更硬了。第三扩展方向要具体不要假大空。写“后续可以做实时推荐系统”没有任何信息量。要写“本实验用静态 CSV 模拟日志后续通过 Kafka 接入实时点击流用 Spark Streaming 替代 MapReduce 做窗口计算每秒处理 5000 条消息”这种具体到技术的扩展。写得出这一步说明你是从“做实验”走到了“想方案”了。我自己的教训是第一次写报告时只贴了 WordCount 的输出结果答辩被问“你的数据源比例是多少”直接愣住。后来养成习惯每次实验记操作日志连docker-compose ps的输出都会留档。这个习惯后来做线上问题排查时也帮了大忙——出了问题能回溯到底哪一步配置改了。希望这份实验报告能成为你从课程走向实战的支点希望帮到你。本文还有配套的精品资源点击获取