
简介本资源是《大数据技术原理与应用》课程配套的‘Spark初级编程实践’实验报告面向高校大数据初学者及Hadoop/Spark入门学习者聚焦分布式计算框架的核心操作能力培养。内容覆盖Spark本地与HDFS环境搭建、spark-shell交互式数据读取与行数统计、基于Scala的三个完整独立应用程序开发SimpleApp行数统计、RemDup数据去重、AvgScore多文件平均成绩计算并附详细排错指南——精准定位URL路径错误、HDFS根目录误用、URI空格异常等典型问题及解决方案。资源为1个1.9MB的DOCX文档结构清晰含实验环境配置清单、分步操作截图共18图、代码片段、运行结果与问题分析便于对照复现与理解底层逻辑。目前已有8339人学习下载是兼顾理论说明、实操记录与调试经验的高实用性入门实践材料。1. Spark初级编程实践不是配环境是搭出能跑通的最小闭环你是不是也经历过——装完 Sparkspark-shell能启动但一读本地文件就报IllegalArgumentExceptionHDFS 路径写成~/test.txt死活找不到文件打包好的 JAR 提交后直接URISyntaxException错误堆栈里连个具体行号都没有这不是你手生是 Spark 的路径语义、上下文绑定和构建链路从第一天起就在埋雷。这份实验报告不是课程作业流水账它是一线工程师拆解过的「Spark 初级实践最小可行闭环」从单机伪分布式环境Windows Ubuntu Kylin 16.04 虚拟机出发覆盖spark-shell交互调试、HDFS 文件读取、Scala 独立应用开发、sbt 构建、JAR 打包与spark-submit部署全链路。它不讲 RDD 原理但告诉你为什么file:///必须是三个斜杠不画 DAG 图但让你看清sc.textFile(hdfs://...)和sc.textFile(/user/hadoop/...)在 Hadoop 3.1.3 下行为为何不同不堆砌 API但把distinct()去重、map().reduceByKey()求平均值这些操作落到真实文件结构、真实目录权限、真实 sbt 依赖版本上。适合刚配好 HadoopSpark 伪集群、想立刻验证“我写的代码真能跑起来”的开发者也适合带学生做实验的导师——所有命令、路径、配置都来自实测截图图3图18所有坑都来自图19图21 的真实报错。2. 环境复现Ubuntu Kylin 16.04 上 Hadoop 3.1.3 Spark 3.x 伪分布式部署实操提示本节所有路径、用户、版本均严格对齐实验报告原文LAPTOP-9KJS8HO6 / ubuntukylin-16.04 / Hadoop 3.1.3 / JDK 1.8。不推荐跳过此步直接用 Docker 或云平台镜像——很多玄学问题如NoClassDefFoundError: org/apache/hadoop/fs/FileSystem只在手动部署的旧版环境中暴露得最彻底。2.1 为什么选 Ubuntu Kylin 16.04 Hadoop 3.1.3这不是怀旧是工程约束下的理性选择。Ubuntu Kylin 16.04 基于 Ubuntu 16.04内核 4.4glibc 2.23完美兼容 Hadoop 3.1.3 编译时的二进制依赖。而 Hadoop 3.1.3 是首个正式支持 Java 8 的稳定版Hadoop 2.x 对 Java 8 支持存在Unsafe类兼容性问题同时 Spark 3.x实验中未明说版本但从scala-2.11和spark-submit参数推断为 3.0.0~3.1.2与之 ABI 兼容性最佳。若强行升级到 Hadoop 3.3会触发org.apache.hadoop.ipc.RemoteException(org.apache.hadoop.security.AccessControlException)权限校验失败——因为 Kylin 16.04 的 OpenJDK 8u292 默认禁用sun.misc.Unsafe而新版 Hadoop 的 RPC 层强依赖它。2.2 Hadoop 伪分布式核心配置core-site.xmlhdfs-site.xmlHadoop 3.1.3 的伪分布式模式关键不在fs.defaultFS而在dfs.namenode.http-address和dfs.datanode.data.dir的路径可写性。实验报告中所有 HDFS 操作路径为/user/hadoop/test.txt这意味着hdfs-site.xml中必须显式配置dfs.namenode.name.dir指向一个当前用户hadoop有完全权限的绝对路径否则hdfs dfs -put会静默失败。!-- $HADOOP_HOME/etc/hadoop/core-site.xml -- configuration property namefs.defaultFS/name valuehdfs://localhost:9000/value /property /configuration!-- $HADOOP_HOME/etc/hadoop/hdfs-site.xml -- configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name valuefile:///usr/local/hadoop/data/namenode/value /property property namedfs.datanode.data.dir/name valuefile:///usr/local/hadoop/data/datanode/value /property /configuration参数说明dfs.replication1伪分布式下只能设为 1设为 0 会直接拒绝格式化设为 2 则start-dfs.sh启动失败DataNode 找不到足够节点。file:///usr/local/hadoop/data/...必须用file://协议前缀且路径为绝对路径若写成./data/namenodeNameNode 格式化时会创建在$HADOOP_HOME下但 DataNode 启动时因权限问题无法写入。执行前务必运行sudo mkdir -p /usr/local/hadoop/data/{namenode,datanode}sudo chown -R hadoop:hadoop /usr/local/hadoop/data2.3 Spark 安装与spark-shell启动验证Spark 不需要独立服务进程即可使用spark-shell但必须正确关联 Hadoop。实验报告中./bin/spark-shell能启动说明$SPARK_HOME/conf/spark-env.sh已配置HADOOP_CONF_DIR# $SPARK_HOME/conf/spark-env.sh export HADOOP_CONF_DIR/usr/local/hadoop/etc/hadoop export SPARK_DIST_CLASSPATH$(/usr/local/hadoop/bin/hadoop classpath)逻辑说明HADOOP_CONF_DIR告诉 Spark 去哪读core-site.xml和hdfs-site.xml缺失则sc.textFile(hdfs://...)报UnknownHostException。SPARK_DIST_CLASSPATH是关键——它把 Hadoop 的 client jar 注入 Spark Classpath。若只设HADOOP_CONF_DIRspark-shell启动时无报错但读 HDFS 时抛ClassNotFoundException: org.apache.hadoop.fs.FileSystem。验证是否生效启动spark-shell后执行sc.version再执行sc.parallelize(Seq(1,2,3)).count()返回3即基础功能正常。2.4 JDK 1.8 版本陷阱与 Eclipse 集成要点实验报告明确使用 JDK 1.8非 11/17这是硬性要求。Spark 3.0.x 编译目标字节码为1.8若用 JDK 11 编译 Scala 代码spark-submit会报java.lang.UnsupportedClassVersionError。Eclipse 中需强制设置Project → Properties → Java Build Path → Libraries → JRE System Library → Alternate JRE → 选择jdk1.8.0_XXXProject → Properties → Java Compiler → Compiler compliance level →1.8血泪经验某次实验中 Eclipse 自动升级了 JRE导致sbt package生成的 JAR 在spark-submit时崩溃错误日志只显示Exception in thread main java.lang.NoClassDefFoundError: scala/Function1——本质是 Scala 2.11.12实验用与 JDK 11 的 lambda 元数据不兼容。永远用java -version和javac -version双重确认终端和 IDE 使用的 JDK 是同一套。3. Spark 数据读取本地文件 vs HDFS 文件的路径语义与权限边界Spark 读取文件不是简单传个路径字符串而是根据 URI Scheme 解析底层 FileSystem 实现。sc.textFile()的第一个参数本质是org.apache.hadoop.fs.FileSystem的实例化依据。理解这个机制才能避开 90% 的“文件找不到”类报错。3.1 本地文件读取file:///是铁律不是可选项实验报告图3中报IllegalArgumentException根源在路径写成/home/hadoop/test.txt。Spark 默认将无协议前缀的路径视为Hadoop LocalFileSystem但它要求路径必须以file://开头且是三斜杠file:///因为file://是标准 URI Scheme表示本地文件系统第三个/是 Authority 字段的占位符代表 host本地即空缺则被解析为file:/home/hadoop/test.txt此时 Spark 尝试连接 hosthome端口hadoop必然失败。✅ 正确写法spark-shell中val sc spark.sparkContext val textFile sc.textFile(file:///home/hadoop/test.txt) // 注意三个斜杠 val lineCount textFile.count() println(s文件行数: $lineCount)参数说明file:///home/hadoop/test.txtfile://是 Scheme/home/hadoop/test.txt是 path/开头表示绝对路径Spark 会调用LocalFileSystem.open()。若文件在 Windows 主机虚拟机中需先scp或共享文件夹挂载路径仍为file:///mnt/hgfs/xxx/test.txt不可用C:\xxx\test.txt。textFile()返回RDD[String]count()是 Action 操作触发实际计算。3.2 HDFS 文件读取hdfs://显式协议 user/hadoop目录约定实验报告图4图5 显示上传文件到/user/hadoop/test.txt而非/test.txt或~/test.txt。这是因为 HDFS 的默认权限模型强制要求每个用户有专属 home 目录/user/{username}且只有该用户有写权限。hdfs dfs -put若指定/test.txt会报Permission denied: userhadoop, accessWRITE, inode/:root:supergroup:drwxr-xr-x。✅ 正确流程终端中# 1. 创建用户目录首次运行 hdfs dfs -mkdir -p /user/hadoop # 2. 上传文件确保 test.txt 存在 echo -e line1\nline2\nline3\nline4 /home/hadoop/test.txt hdfs dfs -put /home/hadoop/test.txt /user/hadoop/ # 3. Spark Shell 中读取两种等效写法 val hdfsFile1 sc.textFile(hdfs://localhost:9000/user/hadoop/test.txt) val hdfsFile2 sc.textFile(/user/hadoop/test.txt) // Spark 自动补全为 hdfs://... val count hdfsFile1.count()逻辑说明hdfs://localhost:9000/...显式指定 NameNode 地址和端口绕过core-site.xml配置适合调试网络连通性。/user/hadoop/test.txtSpark 内部通过FileSystem.get(new Configuration())获取默认 FileSystem即DistributedFileSystem自动拼接fs.defaultFS。hdfs dfs -ls /user/hadoop必须能看到文件否则sc.textFile()报InvalidInputException—— 这是实验报告图20 的根本原因不是 Spark 问题是 HDFS 文件未真正落盘。3.3sc.textFile()底层原理FileSystem 工厂如何决定读取方式sc.textFile(path)的执行流程如下SparkContext 解析path字符串提取 Schemefile,hdfs,s3a等调用org.apache.hadoop.fs.FileSystem.get(URI.create(path), conf)FileSystem工厂根据core-site.xml中fs.{scheme}.impl属性加载实现类file:///→org.apache.hadoop.fs.LocalFileSystemhdfs://→org.apache.hadoop.hdfs.DistributedFileSystemDistributedFileSystem.open()与 NameNode 建立 RPC 连接获取 BlockLocation 列表分片读取。避坑 / 常见问题 / 排查现象1sc.textFile(/user/hadoop/test.txt)报InvalidInputException: Input path does not exist原因HDFS 中该路径确实不存在或hdfs dfs -ls查看时权限不足hadoop用户未被加入hadoop组。解决sudo usermod -a -G hadoop $USER重启终端再hdfs dfs -ls /user/hadoop。现象2sc.textFile(hdfs://localhost:9000/user/hadoop/test.txt)报java.net.ConnectException: Connection refused原因NameNode 未启动或core-site.xml中fs.defaultFS端口与hdfs-site.xml中dfs.namenode.http-address端口冲突如前者 9000后者 9870。解决jps查看 NameNode 进程是否存在hdfs getconf -confKey fs.defaultFS确认端口netstat -tuln | grep 9000检查端口监听。现象3sc.textFile(file:///home/hadoop/test.txt)在spark-shell中成功但在独立应用中报FileNotFoundException原因独立应用运行在 Driver 进程Driver 若不在hadoop用户下启动则/home/hadoop/test.txt权限被拒绝hadoop用户家目录默认700。解决将文件移到/tmp/test.txt全局可读或chmod 744 /home/hadoop/test.txt。4. Scala 独立应用开发从SimpleApp.scala到spark-submit的完整构建链路交互式spark-shell适合调试但生产环境必须用独立应用Standalone Application。实验报告中SimpleApp.scala是最简模板但它暴露出 sbt 构建、依赖管理、主类声明三大关卡。跳过任一环spark-submit就是黑匣子。4.1SimpleApp.scala结构解析Driver 程序的骨架与生命周期// SimpleApp.scala import org.apache.spark.sql.SparkSession object SimpleApp { def main(args: Array[String]) { // 1. 创建 SparkSessionSpark 2.0 推荐兼容 RDD/DF/Dataset val spark SparkSession .builder() .appName(SimpleApp) .master(local[*]) // 本地模式* 表示用所有 CPU 核 .getOrCreate() // 2. 获取 SparkContext用于 RDD 操作 val sc spark.sparkContext // 3. 读取 HDFS 文件注意必须用 hdfs:// 或 /user/hadoop/ val inputPath hdfs://localhost:9000/user/hadoop/test.txt val textFile sc.textFile(inputPath) // 4. 计算行数并打印 val count textFile.count() println(s文件 $inputPath 的行数为: $count) // 5. 关闭资源重要避免 Driver 内存泄漏 spark.stop() } }逻辑说明.master(local[*])实验环境为单机必须显式指定否则默认local仅 1 线程性能极低。*表示自动检测 CPU 核数。inputPath必须与 HDFS 实际路径一致hdfs://协议显式写出更安全避免依赖core-site.xml。spark.stop()是硬性要求。若遗漏下次spark-submit会因端口占用4040失败报java.net.BindException: Address already in use。4.2simple.sbt构建配置Scala 版本、Spark 依赖与打包插件sbt是 Scala 生态标准构建工具simple.sbt不是可有可无的配置文件而是定义整个应用“基因”的清单。// simple.sbt name : a-simple-hdfs-test version : 1.0 scalaVersion : 2.11.12 // 必须与 Spark 3.0.x 二进制兼容 // Spark 依赖provided 表示运行时由 Spark 提供不打入 JAR libraryDependencies Seq( org.apache.spark %% spark-sql % 3.0.0 % provided, org.apache.spark %% spark-core % 3.0.0 % provided ) // 打包插件sbt-assembly 生成 fat-jar addSbtPlugin(com.eed3si9n % sbt-assembly % 0.14.10)参数说明scalaVersion : 2.11.12Spark 3.0.0 编译于 Scala 2.11若用 2.12spark-submit会报java.lang.NoClassDefFoundError: scala/Product。% provided关键若不加spark-sql_2.11-3.0.0.jar会被打入 fat-jar与 Spark 集群自带的同名 jar 冲突导致NoSuchMethodError。sbt-assembly生成包含所有依赖除 provided的单一 JARspark-submit只需提交这一个文件。4.3sbt package与sbt assembly的区别何时用哪个sbt package仅编译源码生成target/scala-2.11/a-simple-hdfs-test_2.11-1.0.jar不含任何依赖。此 JAR 仅含SimpleApp.classspark-submit运行时会报ClassNotFoundException: org.apache.spark.sql.SparkSession。sbt assembly执行sbt-assembly插件将所有非-provided 依赖如自定义工具类与 class 合并生成target/scala-2.11/a-simple-hdfs-test-assembly-1.0.jar。实验报告图8 使用的是sbt package但图9 能运行说明其simple.sbt中已隐含sbt-assembly配置或 Spark 环境变量SPARK_DIST_CLASSPATH已包含所需 jar。✅ 推荐流程确保万无一失# 进入项目根目录含 SimpleApp.scala 和 simple.sbt cd /usr/local/spark/mycode/HDFStest # 1. 清理旧构建 sbt clean # 2. 编译并生成 assembly JAR推荐 sbt assembly # 3. 提交注意 --class 参数是 object 名非文件名 /usr/local/spark/bin/spark-submit \ --class SimpleApp \ --master local[*] \ target/scala-2.11/a-simple-hdfs-test-assembly-1.0.jar避坑 / 常见问题 / 排查现象1sbt assembly报deduplicate: different file contents found in the following:原因多个依赖包含同名资源文件如reference.confsbt-assembly 默认拒绝合并。解决在simple.sbt中添加 deduplicate 策略assemblyMergeStrategy in assembly : { case PathList(META-INF, xs _*) MergeStrategy.discard case x MergeStrategy.first }现象2spark-submit报User class threw exception: java.lang.NoClassDefFoundError: scala/reflect/ClassTag原因scala-reflect未被sbt-assembly包含且未设为provided。解决在libraryDependencies中添加org.scala-lang % scala-reflect % 2.11.12 % provided。现象3spark-submit输出WARN NativeCodeLoader: Unable to load native-hadoop library原因Hadoop 本地库libhadoop.so未找到不影响功能但影响 HDFS 读写性能。解决下载对应 Hadoop 3.1.3 的 native lib解压到$HADOOP_HOME/lib/native并在spark-env.sh中添加export LD_LIBRARY_PATH$HADOOP_HOME/lib/native:$LD_LIBRARY_PATH。5. 数据处理实战去重RemDup.scala与求平均值AvgScore.scala的 RDD 编程范式实验报告中RemDup和AvgScore是两个典型数据处理场景前者是集合运算Union Distinct后者是聚合计算Map ReduceByKey。它们不依赖 DataFrame API纯粹用 RDD 操作直击 Spark 分布式计算本质。但新手常把distinct()当万能药或误用reduce()导致精度丢失——这些坑都在真实代码里埋着。5.1 数据去重RemDup.scala的 Union Distinct 正确姿势// RemDup.scala import org.apache.spark.sql.SparkSession object RemDup { def main(args: Array[String]) { val spark SparkSession .builder() .appName(RemDup) .master(local[*]) .getOrCreate() val sc spark.sparkContext // 1. 读取两个文件路径必须存在 val fileA sc.textFile(file:///home/hadoop/A.txt) val fileB sc.textFile(file:///home/hadoop/B.txt) // 2. 合并 RDDUnion 保持分区不 shuffle val unionRDD fileA.union(fileB) // 3. 去重Distinct 触发 shuffle生成新 RDD val distinctRDD unionRDD.distinct() // 4. 保存结果必须指定目录不能是文件名 distinctRDD.saveAsTextFile(file:///home/hadoop/C.txt_output) println(去重完成结果保存在 C.txt_output 目录) spark.stop() } }逻辑说明union()是 transformation将两个 RDD 的 partition 合并不改变数据不 shuffledistinct()是 action需全局去重必须 shuffle。saveAsTextFile(path)的path必须是目录Spark 会在此目录下生成_SUCCESS和part-00000等文件。若写成C.txt报java.io.IOException: Not a file: /home/hadoop/C.txt。实验报告图10 中A.txt和B.txt内容为键值对如20170101 xdistinct()对整行去重符合需求。5.2 求平均值AvgScore.scala的map().reduceByKey()精度保障// AvgScore.scala import org.apache.spark.sql.SparkSession object AvgScore { def main(args: Array[String]) { val spark SparkSession .builder() .appName(AvgScore) .master(local[*]) .getOrCreate() val sc spark.sparkContext // 1. 读取多个成绩文件假设在 /home/hadoop/datas/ 下 val algorithm sc.textFile(file:///home/hadoop/datas/Algorithm.txt) val database sc.textFile(file:///home/hadoop/datas/Database.txt) val python sc.textFile(file:///home/hadoop/datas/Python.txt) // 2. 合并并解析每行 姓名 成绩 → (姓名, (成绩, 1)) val allScores algorithm.union(database).union(python) .map { line val parts line.trim.split(\\s) if (parts.length 2) { val name parts(0) val score parts(1).toDouble (name, (score, 1.0)) // (姓名, (分数, 计数)) } else { (invalid, (0.0, 0.0)) } } .filter(_._1 ! invalid) // 3. 聚合sum(分数), sum(计数) → (姓名, (总分, 总次数)) val sumCount allScores.reduceByKey { case ((sum1, cnt1), (sum2, cnt2)) (sum1 sum2, cnt1 cnt2) } // 4. 计算平均值保留两位小数 val avgScores sumCount.map { case (name, (total, count)) val avg BigDecimal(total / count).setScale(2, BigDecimal.RoundingMode.HALF_UP).toDouble (name, avg) } // 5. 保存结果 avgScores.saveAsTextFile(file:///home/hadoop/datas/avg_output) // 6. 打印仅用于调试生产环境注释掉 avgScores.collect().foreach(println) spark.stop() } }参数说明reduceByKey()是核心它按 key 分组在每个 partition 内先局部 reduce如(小明, (92,1)) (小明, (95,1)) → (小明, (187,2))再全局 shuffle 合并比groupByKey()效率高 3~5 倍。BigDecimal保证精度Double直接除法如251.0/3可能产生83.66666666666667BigDecimal强制四舍五入到83.67匹配实验报告图18 输出。collect()仅限小数据集调试大数据量会 OOM生产环境必须用saveAsTextFile()。5.3sbt多模块管理当RemDup和AvgScore共存时若将RemDup和AvgScore放在同一项目simple.sbt需支持多子项目避免sbt assembly打包冲突// simple.sbt多模块版 lazy val root (project in file(.)) .aggregate(remDup, avgScore) lazy val remDup (project in file(remdup)) .settings( name : remove-duplication, version : 1.0, scalaVersion : 2.11.12, libraryDependencies Seq( org.apache.spark %% spark-core % 3.0.0 % provided ), assemblyJarName in assembly : remove-duplication-assembly-1.0.jar ) lazy val avgScore (project in file(avgscore)) .settings( name : average-score, version : 1.0, scalaVersion : 2.11.12, libraryDependencies Seq( org.apache.spark %% spark-core % 3.0.0 % provided ), assemblyJarName in assembly : average-score-assembly-1.0.jar )避坑 / 常见问题 / 排查现象1RemDup运行后C.txt_output目录为空或只有_SUCCESS文件原因saveAsTextFile()路径所在父目录如/home/hadoop无写权限或磁盘满。解决ls -ld /home/hadoop检查权限df -h检查磁盘改用/tmp/C_output测试。现象2AvgScore输出平均值为83.66666666666667而非83.67原因未用BigDecimalDouble除法精度丢失。解决严格按上述代码使用BigDecimal(total / count).setScale(2, ...).现象3spark-submit提交AvgScoreJAR 后/home/hadoop/datas/avg_output下part-00000文件内容乱码含中文原因Spark 默认 UTF-8但 Linux 终端 locale 为zh_CN.UTF-8saveAsTextFile()写入时编码正常但cat part-00000显示异常。解决用iconv -f utf-8 -t gbk part-00000转码或统一终端 localeexport LANGen_US.UTF-8。6. 进阶验证与排错技巧用spark-submit --verbose和yarn logs定位黑匣子问题当spark-submit提交后控制台只输出INFO SparkContext: Successfully stopped SparkContext却看不到任何业务日志或者part-00000文件内容为空这就是典型的“黑匣子”状态。别急着重写代码先用 Spark 自带的诊断工具挖出真相。6.1spark-submit --verbose查看完整的 JVM 启动参数与类路径--verbose参数会打印spark-submit构建的完整java命令包括-cpClasspath和-D系统属性这是排查ClassNotFoundException和NoClassDefFoundError的第一现场。/usr/local/spark/bin/spark-submit \ --verbose \ --class AvgScore \ --master local[*] \ /home/hadoop/sparkapp3/AvgScore/target/scala-2.11/average-score-assembly-1.0.jar关键信息定位搜索CLASSPATH确认spark-core_2.11-3.0.0.jar是否在其中应有因设为provided但spark-submit会从$SPARK_HOME/jars/加载。搜索spark.sql.adaptive.enabled确认 Spark SQL 配置是否生效实验中无需但可验证配置注入。搜索java -Xmx确认 Driver 内存是否足够默认1g大数据集需--driver-memory 2g。6.2yarn logs替代方案伪分布式下用log4j直接读取 Driver 日志Hadoop 3.1.3 伪分布式不启用 YARN ResourceManager因此yarn logs -applicationId无效。但 Spark Driver 日志默认输出到$SPARK_HOME/logs/按时间戳命名# 查看最新日志通常为 spark-{user}-org.apache.spark.deploy.master.Master-*.out ls -lt $SPARK_HOME/logs/ | head -5 # 实时跟踪 Driver 日志提交后立即执行 tail -f $SPARK_HOME/logs/spark-hadoop-org.apache.spark.deploy.worker.Worker-*.out日志解读技巧INFO DAGScheduler: Job 0 finished任务成功结束。ERROR Executor: Exception in task某个 Task 执行失败后面紧跟Caused by:堆栈精准定位到AvgScore.scala第几行。WARN SizeEstimator: Failed to check whether ... is serializableRDD 中存了不可序列化对象如java.io.File需检查map()函数内是否引用了外部非 Serializable 变量。6.3 用sc.parallelize()快速验证 RDD 逻辑绕过文件 I/O当怀疑textFile()读取失败或map()解析逻辑有误最快验证方式是跳过文件用内存数据构造 RDD// 在 spark-shell 中快速验证 AvgScore 逻辑 val testData sc.parallelize(Seq( 小明 92, 小红 87, 小新 82, 小丽 90, 小明 95, 小红 81, 小新 89, 小丽 85, 小明 82, 小红 83, 小新 94, 小丽 91 )) val parsed testData.map { line val p line.split(\\s) (p(0), (p(1).toDouble, 1.0)) } val summed parsed.reduceByKey { case ((s1,c1),(s2,c2)) (s1s2, c1c2) } val avgs summed.map { case (n,(s,c)) (n, BigDecimal(s/c).setScale(2,BigDecimal.RoundingMode.H p a hrefhttps://download.csdn.net/download/qq_36428822/86224843 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p