资讯动态

Hadoop+SpringBoot电力数据分析系统搭建实战

发布时间:2026/10/29 17:24:38 来源:尧图企业网站定制
简介基于Hadoop与Spring Boot的电力生产数据分析系统完整项目包面向计算机相关专业学生、毕业设计/课程设计使用者及大数据入门开发者。项目以HDFSYarn为存储与调度底座结合PySpark完成数据预处理和分析后端采用Spring Boot、MyBatis、Druid前端使用Vue适合在电力生产数据场景中学习大数据分析与Web应用整合。压缩包共369个文件约9.6MB主要包含Java后端源码、Vue页面、XML配置、Python脚本、SQL/CSV数据文件、项目截图与说明文档结构清晰便于按模块查阅。包内含项目截图、搭建说明及文档代码经过运行测试下载后可根据README和文档快速还原环境已有166人学习下载适合用于毕设、课设或初期项目演示。1. 从“能跑通的 Demo”到“能答辩的毕设”一套 HadoopSpringBoot 电力生产数据分析系统到底在做什么做电力、能源方向的毕业设计或课程设计时最常见的尴尬是Hadoop 课上学过、Spring Boot 也写过 CRUD但真要做一个“基于 Hadoop 大数据 Spring Boot 实现的电力生产数据分析系统”就卡在两层框架怎么拼起来。这套系统的本质是两条线一条是数据线把电力生产现场产生的测点数据电压、电流、有功功率、设备温度落到 HDFS 上用 MapReduce 或 Spark 之类的批处理任务算出日发电量、负载率、告警统计另一条是业务线Spring Boot 提供 Web 服务和权限控制把分析结果展示给不同角色的用户。它解决的痛点是数据量大、格式杂、多人要看不同范围的数据单靠数据库存和查是撑不住的。适合谁适合想在毕设里真正把 Hadoop 跑起来、而不是只写 Word 的人也适合想进大数据方向但缺一个完整项目经验的在职读者。2. Hadoop 伪分布式环境搭建让电力生产数据先落到 HDFS2.1 版本选型JDK 1.8 Hadoop 3.3.x 与 Spring Boot 2.7.x 的搭配理由大数据集群部署策略里最忌讳的一件事就是为了做毕设直接买三台云主机搭集群。单机伪分布式在配置上和生产集群几乎一致只是所有进程跑在同一台机器上用来理解 HDFS 和 YARN 的工作机制、跑通数据链路完全够用。我一般建议用一台 Linux 虚拟机或云主机2 核 4G 起步装 CentOS 7 或 Ubuntu 20.04。版本搭配是第一个要死记的参数组合JDK 用 1.8Hadoop 用 3.3.xSpring Boot 用 2.7.x。为什么 Spring Boot 不用最新的 3.x因为 Hadoop 生态里有大量依赖还停留在 javax 命名空间Spring Boot 3.x 强制切到 jakarta接 Hadoop 客户端会撞出一片 ClassNotFoundException。这类“Spring Boot 版本太高”引发的问题在真实项目里特别常见后面坑的章节我会专门讲。JDK 1.8 则是 Hadoop 官方支持最稳的版本用 17 跑会有反射警告和权限问题。2.2 三个核心 XML 与启动命令伪分布式其实就这几行配置先把 Hadoop 解压到 /opt/hadoop配好环境变量 JAVA_HOME、HADOOP_HOME然后改四个配置文件。第一个是 core-site.xmlconfiguration property namefs.defaultFS/name valuehdfs://localhost:9000/value /property property namehadoop.tmp.dir/name value/opt/hadoop_tmp/value /property /configurationfs.defaultFS 指定了 NameNode 的地址和端口后面 Spring Boot 连 HDFS 也要用这个地址。hadoop.tmp.dir 非常关键NameNode 的元数据、DataNode 的数据块都默认放在这个目录下后续“二次格式化 NameNode 失败”的坑就是因为它。接下来是 hdfs-site.xml。生产环境通常会配三副本但伪分布式只有一个 DataNode副本数必须显式写成 1configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name value/opt/hadoop_tmp/name/value /property property namedfs.datanode.data.dir/name value/opt/hadoop_tmp/data/value /property /configuration再配 yarn-site.xml 和 mapred-site.xml。YARN 负责资源调度MapReduce 任务跑在它上面端口和资源参数可以参考下面这张表参数推荐值说明yarn.nodemanager.aux-servicesmapreduce_shuffleNodeManager 的辅助服务MapReduce 依赖它yarn.nodemanager.resource.memory-mb2048单机可用的总内存4G 内存的机器别给太多yarn.scheduler.minimum-allocation-mb256单个容器最小内存设太大浪费资源mapreduce.framework.nameyarn让 MapReduce 任务提交到 YARN 执行yarn-site.xml 里把上面前三个参数写进去mapred-site.xml 里写 mapreduce.framework.name。配置完成后如果你的机器内存只有 4G建议把 yarn.nodemanager.resource.memory-mb 降到 1536否则后面跑分析任务时容器会因为内存不足被直接 kill 掉。这一步很多人忽略等 YARN 日志里出现 Container killed by the ResourceManager 才回头调参。2.3 上传一份样例 CSV 验证 HDFS 读写配置写得再完整不启动验证就等于零。第一次启动 HDFS 需要先格式化 NameNode生成元数据目录。注意格式化命令只需要在第一次运行后续每次启动都不需要再执行cd /opt/hadoop bin/hdfs namenode -format sbin/start-dfs.sh sbin/start-yarn.sh jps格式化时如果终端输出 “Successfully formatted” 就是正常。start-dfs.sh 会拉起 NameNode、DataNode 和 SecondaryNameNodestart-yarn.sh 拉起 ResourceManager 和 NodeManager。jps 是 Java 进程查看命令能看到 5 个进程才说明伪分布式起来了。每次开机后只需要跑 start-dfs.sh 和 start-yarn.sh不要重复格式化除非你想把整个 Hadoop 的数据清掉重来。启动后赶紧做一次完整的 HDFS 读写验证避免后面 Spring Boot 连不上才发现问题。把一份电力测点 CSV 放上去再读出来hdfs dfs -mkdir -p /data/raw hdfs dfs -put /opt/samples/station_20250601.csv /data/raw/ hdfs dfs -ls /data/raw hdfs dfs -cat /data/raw/station_20250601.csv | head -20能正常列出文件并打印出 CSV 内容说明 HDFS 这一层已经通了。到这一步电力生产数据的落盘问题就解决了下一步要解决的是 Spring Boot 怎么读写这些数据。3. Spring Boot 接入 HDFS把文件读写与分析任务封装成 REST 服务3.1 引入 hadoop-client 依赖避开 Spring Boot 版本太高带来的命名空间冲突Spring Boot 接入 HDFS 最常见的做法是引入 hadoop-client 依赖通过官方 FileSystem API 操作 HDFS而不需要自己写 Socket 通信。Maven 依赖写法如下dependency groupIdorg.apache.hadoop/groupId artifactIdhadoop-client/artifactId version3.3.6/version /dependency这个依赖会带进来 Guava、commons-logging 等一堆传递依赖而 Spring Boot 的 spring-boot-starter-parent 也会管理 Guava 版本两者容易出现冲突典型报错是 NoSuchMethodError: com.google.common.base.Preconditions.checkArgument。解决方式是在 hadoop-client 的 dependency 里把 Guava 排除掉再显式引入一个固定版本dependency groupIdorg.apache.hadoop/groupId artifactIdhadoop-client/artifactId version3.3.6/version exclusions exclusion groupIdcom.google.guava/groupId artifactIdguava/artifactId /exclusion /exclusions /dependency dependency groupIdcom.google.guava/groupId artifactIdguava/artifactId version27.0.1-jre/version /dependency这里把 Guava 固定在 27.0.1-jre是 Hadoop 3.3.x 官方测试过的兼容版本。需要强调一句Spring Boot 版本太高是这里最大的坑如果你用 3.x还要面对 javax 转 jakarta 的问题hadoop-client 里很多类直接 import javax.*编译期就过不去。所以项目起步阶段直接把 Spring Boot 控制在 2.7.x能省掉一整类莫名其妙的问题。3.2 用 FileSystem API 封装 HDFS 操作上传、列表、下载可以直连拿到依赖之后需要一个配置类把 Hadoop 的 Configuration 和 FileSystem 准备好。伪分布式部署时客户端和 Hadoop 在同一台机器上直接用 fs.defaultFS 就能连接Configuration public class HadoopConfig { Bean public Configuration hadoopConfiguration() { Configuration conf new Configuration(); conf.set(fs.defaultFS, hdfs://localhost:9000); conf.setBoolean(fs.hdfs.impl.disable.cache, true); return conf; } Bean public FileSystem fileSystem() { try { return FileSystem.get(hadoopConfiguration()); } catch (IOException e) { throw new RuntimeException(HDFS FileSystem 初始化失败, e); } } }fs.hdfs.impl.disable.cache 这个参数很多人不知道它的作用是禁用 HDFS 客户端的进程级缓存。如果不设同一个 JVM 里多个用户共用同一个 FileSystem 实例会出现 A 用户写文件、B 用户读到 A 的临时状态的诡异问题。在毕设系统里后续要做行列权限多用户同时访问是常态所以这个开关建议显式设上。封装 FileSystem 之后写一个 HdfsService把常用的上传、列表、读取操作做薄。这是整个系统里复用率最高的一个服务类Service public class HdfsService { Resource private FileSystem fileSystem; public void upload(String path, InputStream input) throws IOException { Path hdfsPath new Path(path); try (FSDataOutputStream out fileSystem.create(hdfsPath, true)) { IOUtils.copyBytes(input, out, 4096, true); } } public ListString listFiles(String dir) throws IOException { Path hdfsDir new Path(dir); FileStatus[] statuses fileSystem.listStatus(hdfsDir); ListString paths new ArrayList(); for (FileStatus status : statuses) { paths.add(status.getPath().toString()); } return paths; } public String readText(String path) throws IOException { Path hdfsPath new Path(path); try (FSDataInputStream in fileSystem.open(hdfsPath)) { return IOUtils.toString(in, StandardCharsets.UTF_8); } } }create(hdfsPath, true) 里的第二个参数是覆盖标记为 true 表示同名文件直接覆盖。这在重新上传同一天的电力数据时很有用免得手工先删旧文件。IOUtils.copyBytes 的第三个参数是缓冲区大小伪分布式环境给 4096 字节足够如果你在跨机房读大文件可以调到 65536但本机跑没差别。readText 里用 UTF-8 读对应后面“中文乱码”那个坑源文件如果不是 UTF-8 编码这里会看到一串问号。3.3 对外接口与原理解释REST Controller 与前端打包联调Spring Boot 的价值是给 HDFS 操作套上一层 HTTP 接口前端页面只跟 Controller 打交道不直接碰 Hadoop API。这里的 Controller 要做得薄只做参数校验和调用转发RestController RequestMapping(/api/hdfs) public class HdfsController { Resource private HdfsService hdfsService; PostMapping(/upload) public String upload(RequestParam(file) MultipartFile file, RequestParam(dir) String dir) throws IOException { String path dir / file.getOriginalFilename(); hdfsService.upload(path, file.getInputStream()); return path; } GetMapping(/list) public ListString list(RequestParam(dir) String dir) throws IOException { return hdfsService.listFiles(dir); } }上传接口的逻辑是先拼 HDFS 路径再把前端传上来的文件流写入 HDFS。dir 参数由前端指定注意做好校验不能接受 .. 之类的上跳路径否则用户能往任意目录写文件。这是权限设计的一部分后面会把数据权限下沉到用户维度这里先留一个口子。联调时有个很实用的做法把前端 Vue 项目 build 出来的 dist 目录整个拷贝到 src/main/resources/static 下再打包 Spring Boot。这样外网只需要开放一个 8080 端口静态页面和 REST 接口由同一个 jar 服务不用额外部署 Nginx。做法是执行 npm run build然后把 dist 里的内容复制到 static 目录重新 mvn package。前后端分离开发、合并部署的模式在企业内部系统里很常见毕设答辩时也能少开一个端口、少一台服务器。4. 电力生产数据分析模块从原始测点到日发电量与设备告警4.1 原始数据建模时间序列字段、文件切分与存储策略电力生产数据是典型的时间序列数据一个火电厂或者风电场每天会产生几十万条测点记录。建模时不要想着直接存进 MySQL而是按照“原始层 → 汇总层 → 应用层”的思路落到 HDFS。原始层按天分目录路径形如 /data/raw/2025/06/01/每天一个或多个 CSV 文件。字段设计参考下表字段名类型说明record_timestring采集时间格式 yyyy-MM-dd HH:mm:ssstation_idstring电厂或电站编号unit_idstring机组编号active_powerdouble有功功率单位 MWreactive_powerdouble无功功率单位 MVarvoltagedouble母线电压单位 kVcurrentdouble电流单位 Aequipment_tempdouble设备温度单位 ℃fault_flagint告警标记0 正常1 告警文件切分有个经验按 station_id 分目录不要把所有电站堆在一个大文件里。这样后续做行权限拦截时可以直接按目录范围控制用户可见数据扫描成本和权限逻辑都更简单。每 50 万行左右切一个文件HDFS 的块大小默认 128MCSV 能压缩到接近块大小既不浪费空间也不产生大量小文件。4.2 用 MapReduce 跑日发电量统计再把结果写回 HDFS分析模块的核心指标是日发电量。因为数据已经按天分目录只要把当天 CSV 里有功功率累加起来再乘以采集时间间隔就是该站当天的发电量。这里 MapReduce 的 Mapper 负责解析每行记录Reducer 负责分组求和public class DailyPowerMapper extends MapperLongWritable, Text, Text, DoubleWritable { private Text outKey new Text(); private DoubleWritable outValue new DoubleWritable(); Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line value.toString(); String[] fields line.split(,); if (fields.length 4) { return; } String date fields[0].substring(0, 10); String stationId fields[1]; outKey.set(date _ stationId); outValue.set(Double.parseDouble(fields[3])); context.write(outKey, outValue); } }Mapper 的输入是 HDFS 文件的一行文本输出 key 是“日期_电站编号”value 是有功功率。这里有个容易翻车的点CSV 字段顺序如果和建模表不一致取 fields[3] 就不是有功功率。我一般会先在代码里加一个字段映射的配置而不是硬编码下标否则上游调度系统哪天调了列顺序分析结果就悄悄错了。Reducer 侧只需要把同一 key 的数据累加再乘以采集间隔假设 5 分钟一条那一天的采集次数是 288public class DailyPowerReducer extends ReducerText, DoubleWritable, Text, Text { Override protected void reduce(Text key, IterableDoubleWritable values, Context context) throws IOException, InterruptedException { double sum 0; for (DoubleWritable val : values) { sum val.get(); } double powerMwh sum / 12.0; context.write(key, new Text(String.format(%.2f, powerMwh))); } }这里 sum 的单位是 MW × 条数除以 12 是因为 5 分钟一条、一小时 12 条结果就是 MWh。如果采集频率变了这个分母要跟着变否则发电量会偏大或偏小。把这段逻辑做成配置文件里的一个参数比写死在 Reducer 里好得多。MapReduce 计算结果会写回 HDFS 的 /data/result 目录Spring Boot 端只需要用上一章的 readText 方法把结果文件读出来转成 JSON 给前端画曲线图就行。4.3 定时调度与行列权限Quartz 触发分析注解拦截数据权限分析任务不能每天靠人手工点按钮要定时调度。常见做法是在 Spring Boot 里集成 Quartz每天早上 0 点半触发当天的汇总计算。调度任务类的核心逻辑就是提交 MapReduce然后睡上一段时间轮询结果文件是否生成Component public class AnalysisJob extends QuartzJobBean { Resource private HdfsService hdfsService; Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { String today LocalDate.now().toString(); String resultPath /data/result/ today /summary.csv; try { Process process Runtime.getRuntime().exec( /opt/hadoop/bin/hadoop jar /opt/jars/power-analysis.jar DailyPowerJob today); process.waitFor(); String result hdfsService.readText(resultPath); // 把 result 写入 MySQL 供前端快速查询 } catch (Exception e) { throw new JobExecutionException(分析任务失败, e); } } }调度任务的写法不复杂但要考虑两点一是任务执行时间和数据就绪时间的关系如果电厂的数据凌晨 1 点才传完调度时间要往后挪二是任务失败的重试机制Quartz 里配置 misfire 策略时我给单机项目推荐 withMisfireInstructionDoNothing避免补跑堆积导致 YARN 资源耗尽。权限设计是这个系统的门面。参考大数据行列权限设计开源的常见做法行权限管“看得到哪些电站”列权限管“每列能不能看”。实现方式是自定义一个 DataPermission 注解打在查询接口上由 AOP 拦截器根据当前登录用户的 station_id 自动拼接过滤条件Override public ListMapString, Object queryDailySummary(String userId) { User user userMapper.selectById(userId); String sql SELECT * FROM daily_summary WHERE 11; if (StringUtils.hasText(user.getStationScope())) { sql AND station_id IN ( user.getStationScope() ); } // 列权限普通用户隐藏设备温度字段 if (operator.equals(user.getRole())) { sql sql.replace(*, record_time, station_id, active_power); } return jdbcTemplate.queryForList(sql); }行权限用 IN 条件限制电站范围列权限用投影字段控制可见列。这个设计的好处是底层 SQL 完全可控不引入额外框架毕设答辩时也容易讲清楚。注意这里的 SQL 是动态拼接的参数必须来自后端解析出的用户信息绝不能直接用前端传来的字符串否则就是 SQL 注入。5. 项目搭建避坑从代码能编译到系统能跑的 5 个高频问题5.1 二次格式化 NameNode 失败先清空 name/data 目录再格式化现象是第一天跑得好好的第二天想清空数据重新来执行 hdfs namenode -format 后控制台报错说 NameNode 已经在运行或者 layout version 不一致起服务时 DataNode 一直连不上。原因是格式化只清空了元数据可 DataNode 的 data 目录里还残留着旧的块信息两者一比对就对不上。解决是先停掉所有服务删除 hadoop.tmp.dir 下的整个目录再重新格式化。命令顺序是sbin/stop-all.sh rm -rf /opt/hadoop_tmp bin/hdfs namenode -format sbin/start-dfs.sh sbin/start-yarn.sh后期如果你只想清某个业务数据千万别用重新格式化这招直接用 hdfs dfs -rm -r /data/raw 删除对应目录即可。把格式化当成一个危险操作能不动就不动。5.2 Spring Boot 版本太高导致 Guava 和 javax 命名空间报错现象是 mvn package 时编译通过但启动 Spring Boot 后一调用 HDFS 接口就抛 NoSuchMethodError 或 ClassNotFoundException堆栈里能看到 com.google.common 或 javax.servlet 的字样。原因有两层一是 Spring Boot 的依赖管理和 hadoop-client 传递依赖的 Guava 版本不一致二是 Spring Boot 3.x 全面迁移 jakarta 命名空间而 Hadoop 3.3.x 的客户端组件仍依赖 javax。解决就是踩住两点Spring Boot 用 2.7.x 而不是 3.xhadoop-client 里排除 Guava 并显式引入兼容版本。这两个操作落实了九成以上的依赖冲突会消失。剩下那一成用 mvn dependency:tree 查具体是哪个依赖带来的冲突再单独排除。5.3 伪分布式下数据副本不足把 dfs.replication 设为 1现象是 HDFS 管理页面显示块副本数不完整上传文件后 DataNode 日志里反复出现 “There are 1 datanode(s) running and 1 node(s) are excluded in this operation”文件状态一直处于 under-replicated。原因是 Hadoop 默认副本数是 3但伪分布式只有一个 DataNode无法满足副本策略于是 NameNode 一直尝试复制却永远无法成功。解决是在 hdfs-site.xml 里把 dfs.replication 显式设为 1 并重启 HDFS 服务。这个参数属于“不配置就永远在报警”的类型做完这一步HDFS 页面上的健康状态才会变成绿色。5.4 YARN 容器内存不够调小 minimum-allocation 与 map 内存现象是提交 MapReduce 任务后任务跑几秒就失败YARN 日志显示 Container killed by the ResourceManager或者物理内存超出限制。原因是 2 核 4G 的机器上YARN 默认给 NodeManager 分配的资源偏大而 MapReduce 默认的 map 容器内存是 1G多个 map 同时跑直接把机器内存挤爆。解决是调低 yarn-site.xml 里的三个参数给一份我常用的配置property nameyarn.nodemanager.resource.memory-mb/name value2048/value /property property nameyarn.scheduler.minimum-allocation-mb/name value256/value /property property nameyarn.scheduler.maximum-allocation-mb/name value1024/value /property同时把 mapreduce.map.memory.mb 和 mapreduce.reduce.memory.mb 设为 512。这个调参组合能够让单机同时跑 3 到 4 个容器而不触发 OOM。日志里如果还有 “Physical memory usage is very high”继续往下调容器内存别硬扛。5.5 中文乱码统一编码比事后转码省事现象是前端页面上设备名称、告警信息全部显示成问号Spring Boot 日志里读出 HDFS 文件时中文乱成一团。原因是现场导出的 CSV 常常是 GBK 编码而 Hadoop 文件系统读写默认按 UTF-8 处理两端对不上。解决分两步一是在数据导入环节用脚本统一转成 UTF-8 再上传 HDFS二是在 Mapper 里如果实在要直接读 GBK 文件用下面的方式指定编码import java.io.InputStreamReader; InputStreamReader reader new InputStreamReader( ((FileSplit) context.getInputSplit()).getPath().getFileSystem(context.getConfiguration()) .open(((FileSplit) context.getInputSplit()).getPath()), GBK);但我的经验是转码环节越靠前越好最好数据从电力系统导出时就用 Python 脚本清洗成 UTF-8 标准格式。编码的坑属于“事后查半天、事前一分钟”的典型不要在映射和解析阶段硬扛编码问题。6. 跑起来以后验证指标、调 YARN 参数、再往实时分析走一步6.1 先验证闭环再谈功能扩展系统能启动、页面能打开只说明没崩不代表数据链路是对的。我每次做完这类系统都会按下面这个顺序做一次闭环验证第一用 curl 调用上传接口传一个构造的 5 分钟间隔 CSV第二触发分析任务等结果文件生成第三调查询接口核对日发电量是否等于手工计算的期望值第四用两个不同 station_id 的用户登录确认行权限确实过滤了数据。验证命令可以用这一条串起来curl -F filestation_test.csv -F dir/data/raw/2025/06/01 http://localhost:8080/api/hdfs/upload curl http://localhost:8080/api/daily/summary?date2025-06-01如果第二个接口返回的发电量和你手工算的对不上优先怀疑 Mapper 里的列下标和 Reducer 里的采样间隔分母而不是怀疑 Hadoop 算错。大数据系统的调试逻辑是“先怀疑自己的业务代码再怀疑框架”这条血泪经验帮我少走了很多弯路。6.2 调优与下一步批处理往 Spark、Flink 迁移时哪些思路不变调优不用一次做满优先把第 5 章的内存参数稳定住然后根据任务耗时决定要不要动并行度。任务慢的时候先看 YARN 界面上的 map 数量如果文件块多而 map 少可以在提交任务时加 -D mapreduce.job.maps4 提高并行度但单机 CPU 核数不够时这个参数反而有害。调优的原则是先内存再并行度最后才考虑换框架。这个系统的批处理路径如果将来要升级把 MapReduce 换成 Spark 或 Spring Boot 整合 Flink 时思路是不变的数据仍然在 HDFS分析逻辑仍然是从原始层算到汇总层Web 端仍然只读结果。变的只是计算引擎的 API 和提交方式。真正要提前做好的设计是原始数据的目录结构和字段规范只要这一层稳定后面的计算引擎换起来都很快。我做第一个电力数据分析项目时最吃亏的地方是把大部分时间花在代码编译上忽略了对数据的理解。等到答辩时被问“为什么用 HDFS 而不用 MySQL”才发现自己根本讲不清数据量和查询模式之间的取舍。后来我习惯先把数据想象成一条河流先让水流动起来、验证每个环节再回头优化细节。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑