简介本资源是沈阳航空航天大学2024年大数据实训课程配套的综合性项目设计源码面向高校大数据方向本科生及初阶开发者旨在通过真实工程实践强化数据采集、处理、存储、可视化与前后端协同开发等全链路能力。压缩包共542个文件总大小95.44MB涵盖81个Java后端模块含Spring Boot API、45个JavaScript脚本与36个Vue组件支撑响应式前端、50个HTML页面及118张PNG图表资源辅以30个SQL建表与查询脚本、36个XML配置文件、8个TypeScript类型定义及3个Python数据处理脚本如csv2mysql.py完整复现了从CSV数据导入MySQL、Hadoop平台集成到ECharts可视化的大数据典型流程。已有375人学习下载资源结构清晰含Vue应用骨架、Layui与Animate样式体系、多环境配置yml/properties及README说明文档可直接运行调试是理解企业级大数据项目组织方式与技术栈协同的理想实操范本。 从2024年沈阳航空航天大学大数据实训里的综合性项目一路做下来最大的体会就是“综合”这两个字的分量。我们这次不是写几个MapReduce样例或者调通一个SQL这么简单而是完整实现了一套“航班数据分析平台”包括仿真数据生成、Flume日志采集、Kafka消息缓冲、HDFS分布式存储、Hive数仓分层、Spark SQL离线清洗聚合最后通过SpringBoot后端加ECharts前端做可视化展示。整套源码我整理下来差不多有180多个文件代码量说不上夸张但确实覆盖了企业离线数仓的完整链路。这篇文章不是课程报告的复述而是基于我实际调试经验写成的一篇复盘。如果你也在准备大数据综合实训、毕业设计或者求职时想给简历补充一个有说服力的项目可以参考这里的设计思路、源码结构和避坑点。我尽量按照项目从零搭建的顺序来讲同时把最容易出问题的环境、版本、内存配置这些细节一起说清楚。1. 项目整体设计不是功能堆砌而是业务驱动1.1 业务场景怎么定综合性项目最忌讳的就是“为了用技术而用技术”所以第一步不是选框架而是先把业务场景想明白。我们这次围绕“航班数据分析”来做原因主要有三点一是场景本身有数据层次感航班明细、航线维度、机场维度、天气维度之间可以拼出很多业务关联二是航校背景决定了团队对业务理解的起点高三是这个场景在两到三周实训周期内完全可以做完不会因为范围过大导致烂尾。具体业务需求梳理成四个模块航班准点率统计分析按月、按航司、按机场统计准点率、延误率。航线流量排行分析热门航线、出发到达机场排名。延误时长分布统计不同延误区间的航班数量。航班量趋势观察整体航班量随时间的变化趋势。每一个业务指标都可以落到一张宽表或者聚合表上数仓层级的划分就变得很自然。1.2 总体架构应该怎么搭架构的取舍是综合项目里最容易纠结的地方。参考了对标企业数仓的常见做法以后我们最终选了一条离线批处理为主的链路数据生成器 → 本地日志文件 → Flume监控目录 → Kafka → Flume消费者 → HDFS → Hive ODS层 → Spark SQL清洗构建DWD → Hive DWS层聚合 → MySQL结果表 → SpringBoot API → ECharts展示有人可能会问为什么有了Kafka还要用Flume消费因为实训阶段数据量没那么大用Kafka做削峰填谷其实够用了但Flume到HDFS的写入路径调试起来更直观日志也好排查。而且把Kafka引入链路至少让大家理解了Producer和Consumer的角色关系这比单纯Flume直接落HDFS更有教学收益。整条链路里离线分析是主体实时性不是核心诉求。如果你也想在项目里加实时模块建议只保留一个“实时航班延误状态看板”作为亮点即可不需要把整条链路都改造成流式否则实训时间根本不够。1.3 数据量设计不能拍脑袋实训项目的评审和面试官都会问一个问题你的数据量是多少如果只有几千条随便用Excel就能算根本没有上Hadoop集群的意义。所以我们写了一个多线程数据生成器模拟三个月约400万条航班记录原始日志文本超过2GB。这个量级对虚拟机集群来说既不会撑爆磁盘又能体现分布式计算和数仓分层的价值。生成器模拟了航班号、起飞机场、到达机场、计划起飞时间、实际起飞时间、计划到达时间、实际到达时间、延误时长、航司、天气状态等字段。为了后续数据清洗有内容可做我在生成时有意识注入了大约8%的脏数据包括空时间字段、航班号不合法、时间顺序颠倒等这些在后面清洗环节都成了典型的处理样例。2. 技术选型与集群部署版本兼容才是硬道理2.1 组件版本怎么搭配这部分我踩过很深的坑直接上我们验证过的组合组件版本说明JDK1.8全链路统一不建议换更高版本Hadoop3.1.3Namenode HA不需要但HDFS和YARN必须稳定Hive3.1.2与Hadoop3.x兼容好方便跑Hive SQLSpark2.4.0配合Scala 2.11对教学代码兼容性最友好Kafka2.11-2.4.1用2.11版本号避免与Scala库冲突Flume1.9.0注意和Kafka客户端版本配套MySQL8.0存业务结果表SpringBoot2.3.x后端APIECharts4.x前端图表这里特别说一下Spark版本。选Spark2.4.0不是因为新而是因为大多实训项目已有的样例代码、Jar包依赖都是基于Scala 2.11编译的。Spark 3.x默认用Scala 2.12如果你从网上找的代码是2.11编译的直接跑大概率报ClassNotFoundException。如果坚持用Spark 3.x就要做好自己重新编译源码的心理准备实训阶段没必要折腾这个。2.2 集群规模规划我们没有用单机伪分布因为综合项目要求体现分布式单机跑通说服力不足。使用了四台虚拟机一台Master三台Worker节点角色内存分配核心职责node01NameNode, ResourceManager6GB管理元数据和资源调度node02DataNode, NodeManager4GB存储与计算node03DataNode, NodeManager4GB存储与计算node04DataNode, NodeManager4GB存储与计算、跑可视化服务每台虚拟机分配了两颗CPU核心磁盘40GB。这里有个容易忽视的点Yarn的内存配置不能直接用默认值。默认的yarn.nodemanager.resource.memory-mb通常会自动识别宿主机的总内存但虚拟机显示的内存可能不一致导致容器启动后OOM。我最后在yarn-site.xml里手工指定了参数防止NodeManager占用过多内存导致HDFS和Hive被挤爆。2.3 部署过程必须注意的细节部署Hadoop集群时最容易被忽略的是SSH免密、hosts映射、防火墙三个环节。SSH免密不配好每次start-dfs.sh都要输好几次密码烦人且容易中断。hosts不映射很多组件之间通过主机名通信时会解析失败。防火墙不关或者不加规则跨节点数据传输时报Connection refused。另外JDK8的环境变量要在所有节点保持一致hadoop-env.sh里的JAVA_HOME建议写死绝对路径而不是依赖系统的JAVA_HOME。别问为什么实训那天至少有五个人因为环境变量没生效导致DataNode起不来。3. 核心模块实现从仿真数据到可视化3.1 数据生成器怎么写数据生成是用Java写的一个多线程程序核心思路是事先准备机场表、航线表、航司表然后构建几十个维度组合在时间范围内循环生成航班记录写入纯文本日志。举个例子生成器的核心方法大致是这样的public class FlightDataGenerator { private static final ListString AIRLINES Arrays.asList(CA, MU, CZ, HU, 3U); private static final ListString WEATHERS Arrays.asList(晴, 多云, 小雨, 雷暴, 大雾); public FlightRecord generate() { FlightRecord record new FlightRecord(); record.setFlightNo(airline() RandomUtil.randomInt(1000, 9999)); record.setDepartAirport(randomAirport()); record.setArriveAirport(randomAirport()); record.setPlanDepartTime(randomTimeInRange()); int delay generateDelay(); record.setActualDepartTime(record.getPlanDepartTime() delay); // ... return record; } private int generateDelay() { // 天气差时延迟概率提高 if (雷暴.equals(currentWeather)) { return RandomUtil.randomInt(20, 180); } return RandomUtil.randomInt(0, 60); } }多线程部分用了一个固定线程池200个线程并发写文件每个线程独立负责一个日期分片避免同一文件并发写入的顺序错乱。生成的日志文件以日期命名Flume通过spooldir监控目录文件一旦生成就会被采集走。3.2 Flume与Kafka接入Flume的Source配置用的是spooldir如果用的是taildir也是可以的spooldir更简单但注意文件一旦放进去就不能再修改否则会重复采集。我们为了让HDFS上的目录能按天分区用到了Flume的拦截器或者直接通过header格式化到HDFS路径。Kafka的接入相对简单我们分了两段第一段Flume把日志文件发到Kafka的flight-log topic第二段再起一个Flume进程从Kafka消费写入HDFS。这样安排的好处是数据链路里有了消息队列缓冲同时保留了Flume直接落HDFS的直观性。如果你不想用Kafka第二段直接用第一个Flume的HDFS sink也可以但少了Kafka这个中间缓存后续要加Spark Streaming实时消费就没入口了。3.3 数仓怎么分层这是整个综合项目里最核心的呈现点。我们按照标准数据仓库思想划分了三层ODS层原始数据存放区表结构和日志文件字段一一对应不做任何加工。DWD层清洗和标准化后的明细数据处理字段缺失、日期格式统一、业务主键校验。DWS层按业务维度聚合的结果数据比如按航司、月份、机场组合统计准点率、平均延误时长。ODS层的建表语句就是最普通的建表存储格式用TextFile方便查错。DWD层在Spark SQL里做清洗输出格式改成了Parquet列式存储对后续查询性能提升非常明显。清洗逻辑的主要处理有-- DWD层航班明细表清洗空值、统一时间格式 INSERT OVERWRITE TABLE dwd_flight_detail PARTITION (dt) SELECT flight_no, airline_code, depart_airport, arrive_airport, from_unixtime(plan_depart_ts, yyyy-MM-dd HH:mm:ss) AS plan_depart_time, from_unixtime(actual_depart_ts, yyyy-MM-dd HH:mm:ss) AS actual_depart_time, CASE WHEN delay_minutes 0 THEN 0 ELSE delay_minutes END AS delay_minutes, -- 航班准点判断延误小于15分钟视为准点 IF(delay_minutes 15, 1, 0) AS is_ontime, weather, dt FROM ods_flight_log WHERE flight_no RLIKE ^[A-Z0-9]{2}\\d{3,4}$ AND plan_depart_ts IS NOT NULL AND actual_depart_ts IS NOT NULL AND plan_depart_ts actual_depart_ts;这里有个小知识点民航通常把航班延误15分钟以内视为准点这个业务规则直接影响准点率指标口径项目复盘和面试被问到时要能解释清楚。我们原本直接用延误是否大于0来判断后来分析需求时才改成15分钟阈值说明业务理解不到位的话数仓指标会偏。DWS层的聚合表则是直接面向报表需求例如INSERT OVERWRITE TABLE dws_airline_month_stats SELECT airline_code, substr(dt, 1, 7) AS month, COUNT(*) AS total_flights, SUM(is_ontime) AS ontime_flights, ROUND(SUM(is_ontime) * 100.0 / COUNT(*), 2) AS ontime_rate, ROUND(AVG(delay_minutes), 2) AS avg_delay_minutes FROM dwd_flight_detail GROUP BY airline_code, substr(dt, 1, 7);这个聚合结果再从Hive导出到MySQL里面前端通过REST接口读取查询响应就是毫秒级。3.4 数据倾斜与Spark调优实训数据量不算大但我们在按机场分组时发现个别热点机场数据明显偏多跑任务时某些ReduceTask比其他Task慢很多。这就是典型的数据倾斜。处理办法用了两个一是两阶段聚合先把数据加随机前缀分散到不同分区做初步聚合再去掉前缀做第二次聚合。二是调整Spark SQL的分区数设置spark.sql.shuffle.partitions200让并行度匹配集群CPU资源。还有一个小技巧是尽量用Parquet加分区裁剪查询时只读取相关分区文件而不是全表扫描。实训阶段数据量小可能感觉不明显但面试时能讲出这个优化逻辑项目档次就上来了。4. 源码目录设计与工程化习惯4.1 项目目录怎么组织综合性项目源码的目录设计直接影响老师或面试官的第一印象。我们把所有代码放在一个bigdata-project目录下按模块划分清晰bigdata-project/ ├── README.md ├── datagen/ # 仿真数据生成器 │ ├── src/ │ └── conf/ ├── etl/ # ETL脚本和Flume配置 │ ├── flume/ │ ├── kafka/ │ └── hive_sql/ ├── spark/ # Spark SQL清洗任务 │ ├── src/main/scala/ │ └── pom.xml ├── web/ # 可视化后端和前端 │ ├── backend/ │ └── frontend/ ├── docs/ └── sql/目录清晰以后各组员分工也容易对齐。数据组、清洗组、可视化组各自维护自己的模块最后合并时不会有大量冲突。4.2 值得复用的工具类写综合性项目时公共工具类越早抽出来越好。我们一共封装了三个核心工具类第一个是日志格式化工具负责统一日志输出的分隔符和时间格式。第二个是数据校验工具在清洗前检查字段合法性比如航班号正则校验、时间戳范围校验。第三个是JDBC工具封装MySQL连接和批量插入。拿批量插入为例最初用JDBC逐条插入聚合结果10万条数据要跑十几分钟。后来改成PreparedStatement批量提交每500条提交一次整体耗时降到不到一分钟。这个优化无论是答辩还是写简历都值得写进去。4.3 文档和注释怎么处理实训源码最容易出现的问题是“能跑但看不懂”。我的习惯是每个关键类顶部写清楚职责、输入输出、调用关系每个Shell脚本和SQL文件头部都注释清楚功能和使用方式。README里要写清环境要求、部署步骤、启动顺序。不要指望别人能靠猜来看懂你的代码文档本身就是工程能力的一部分。5. 实训过程中最常见的6个问题这部分整理一下我们在实训现场真实遇到过的故障基本都是网上很难直接搜到答案的场景。5.1 Hive运行卡在log4j初始化现象是Hive执行任何命令都会停在log4j:WARN然后长时间没反应。原因是虚拟机hostname解析有问题Hive在启动时尝试通过hostname获取主机信息如果/etc/hosts里没有本机映射会有超时重试。解决办法是在/etc/hosts里加一行本机IP和hostname的映射同时确认hadoop用户对/tmp目录有写权限。5.2 Spark任务OOM报错是ExecutorLostFailure或Container killed by YARN for exceeding memory limits。排查下来是Executor内存默认配置不合理。在Spark任务提交时加上spark-submit \ --master yarn \ --deploy-mode client \ --driver-memory 2g \ --executor-memory 2g \ --executor-cores 2 \ --num-executors 3 \ --conf spark.sql.shuffle.partitions200 \ --class com.bigdata.etl.FlightETLJob \ flight-etl.jar这里有个原则所有Executor内存总和不要超过NodeManager的可用内存。如果你的Worker节点内存只有4GB每个Executor给2GB一个节点只能跑一个Executor再多也会被Yarn杀掉。5.3 HDFS文件夹权限不足执行Spark写入Hive表时报Permission denied。最简单的解决办法是设置HDFS递归权限或者使用具有hadoop组权限的用户执行hdfs dfs -chmod -R 755 /user/hive/warehouse hdfs dfs -chown -R hive:hadoop /user/hive/warehouse5.4 时间解析异常因为生成的脏数据里有大量不符合yyyy-MM-dd HH:mm:ss格式的字段Spark SQL执行cast时会返回NULL但后续计算又用到这个字段导致结果全部错误。排查办法是在清洗SQL里先用RLIKE匹配格式再进入时间字段清洗分支。这是一个很典型的ETL思路先过滤再转换而不是直接转换完再清洗。5.5 Flume采集后HDFS文件大小很小Flume默认sink到HDFS的文件滚动策略是按时间或按events数导致每个文件可能只有几KB、几百KB。调整以下参数a1.sinks.k1.hdfs.rollInterval 60 a1.sinks.k1.hdfs.rollSize 134217728 a1.sinks.k1.hdfs.rollCount 0这样让文件在到128MB或60秒后再滚动避免小文件过多。小文件过多会拖慢NameNode内存和后续Spark读取效率这是实训里最容易被忽略的性能点。5.6 前端接口超时可视化页面加载时图表要等很久原因是后端实时从Hive查询而Hive查询每次都要起Yarn任务延迟可能在几秒到几十秒。优化方案很简单就是报表模块先通过Sqoop或Spark SQL聚合结果落到MySQL后端只查MySQL。这个方案其实也符合真实数仓架构中“结果数据服务化”的思路答辩时能讲清楚会加分。6. 一些个人体会和后续改进方向整套综合项目源码做下来我最深的体会是一个项目的“架构感”比“能跑通”重要得多。刚开始我们也想直接在网上抄一份现成的电商数仓代码但后来发现没有自己改过一环老师问到底层原理的时候完全接不上话。反而是一点一点搭起来以后HDFS的副本策略、Yarn的资源调度、Hive分区为什么能提升查询速度都有了具体的体感。个人建议各位在做类似实训或者以此作为毕业设计蓝本时一定要自己动手重写一遍核心ETL脚本特别是数据清洗逻辑。这里的数据倾斜、字段校验、时间处理方式基本就是面试大数据开发岗位时最常问的细节。源码不是你写得多花哨而是你讲得出每一段代码为什么这么写。后续如果时间允许我打算在这个项目基础上扩展两个方向第一个是接入Spark Streaming把新增的航班数据实时统计到Redis形成准实时看板这样可以把Lambda架构体现出来第二个是把数据质量校验做成自动化脚本在每天ETL前先跑数据质量检查不通过就告警。这些扩展对数据仓库项目的完整度和面题深度都有帮助。最后再分享一个实训时养成的习惯每完成一个模块就把跑通的命令、遇到的问题、解决办法写进自己的笔记里。别看这个动作简单实训结束整理项目报告、做简历项目描述、面试前回顾项目细节时这些笔记就是最宝贵的资料比任何模板都有用。本文还有配套的精品资源点击获取