spark速记
1.spark是什么?
Spark是基于内存计算的大数据并行计算框架。
2.spark部署
spark部署其实就是spark程序逻辑在谁提供的资源里面运行
spark UI 界面解析:https://cloud.tencent.com/developer/article/2633212
3.spark API架构层次:
┌─────────────────────────────────────────────────────────────────┐
│ 用户应用层 │
├─────────────────────────────────────────────────────────────────┤
│ Structured Streaming │ MLlib/ML │ GraphX │ ← 高层 API
├─────────────────────────────────────────────────────────────────┤
│ DataFrame / Dataset │ ← 推荐日常使用
│ (Catalyst 优化器 + Tungsten 执行引擎) │
├─────────────────────────────────────────────────────────────────┤
│ RDD │ ← 底层抽象
│ (核心抽象,所有高层 API 都基于 RDD) │
├─────────────────────────────────────────────────────────────────┤
│ Spark Core │
│ (任务调度、内存管理、容错恢复) │
└─────────────────────────────────────────────────────────────────┘
4.部署方式:

部署命令示例:
spark-submit \ --master yarn \ --deploy-mode cluster \ --class com.example.WordCount \ --num-executors 20 \ --executor-cores 4 \ --executor-memory 8g \ --driver-memory 4g \ --queue etl \ --conf spark.yarn.maxAppAttempts=3 \ hdfs:///apps/spark-demo.jar # cluster模式 jar 必须在HDFS上
5.RDD
4.1 RDD 核心属性
-
分区列表:数据被切成多个 Partition
-
计算函数:每个 Partition 如何计算(
compute) -
依赖列表:对其他 RDD 的依赖关系(窄依赖 / 宽依赖)
-
分区器:可选,定义数据如何分区(如 HashPartitioner)
-
首选位置:可选,分区优先计算的位置(如 HDFS Block 所在节点)
4.2 创建 RDD
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.SparkConf;
SparkConf conf = new SparkConf().setAppName("RDDDemo").setMaster("local[*]");
JavaSparkContext sc = new JavaSparkContext(conf);
// 方式1:从集合创建
JavaRDD<Integer> numbers = sc.parallelize(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
// 指定分区数
JavaRDD<Integer> numbers4 = sc.parallelize(Arrays.asList(1, 2, 3, 4, 5), 4);
// 方式2:从外部文件创建
JavaRDD<String> lines = sc.textFile("hdfs:///data/logs.txt"); // HDFS
JavaRDD<String> localLines = sc.textFile("data/sample.txt"); // 本地文件
// 支持通配符
JavaRDD<String> allLogs = sc.textFile("hdfs:///data/logs/*.log");
4.3 RDD 算子分类
Transformation(转换)—— 惰性执行,返回新 RDD
| 算子 | 说明 | 示例 |
|---|---|---|
map |
逐元素变换 | rdd.map(x -> x * 2) |
flatMap |
逐元素展开 | rdd.flatMap(line -> Arrays.asList(line.split(" ")).iterator()) |
filter |
过滤 | rdd.filter(x -> x > 5) |
distinct |
去重 | rdd.distinct() |
groupByKey |
按 Key 分组 | pairRDD.groupByKey() |
reduceByKey |
按 Key 聚合 | pairRDD.reduceByKey((a, b) -> a + b) |
sortByKey |
按 Key 排序 | pairRDD.sortByKey() |
join |
等值连接 | rdd1.join(rdd2) |
union |
合并 | rdd1.union(rdd2) |
repartition |
重分区 | rdd.repartition(10) |
coalesce |
减少分区(不shuffle) | rdd.coalesce(2) |
Action(行动)—— 触发执行,返回结果
| 算子 | 说明 | 示例 |
|---|---|---|
collect |
收集所有元素到Driver | rdd.collect() |
count |
计数 | rdd.count() |
take |
取前N个 | rdd.take(5) |
first |
取第一个 | rdd.first() |
reduce |
全局聚合 | rdd.reduce((a, b) -> a + b) |
foreach |
逐元素操作(不返回) | rdd.foreach(x -> System.out.println(x)) |
saveAsTextFile |
保存到文件 | rdd.saveAsTextFile("output/") |
4.4 完整案例:WordCount
package com.example;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.SparkConf;
import scala.Tuple2;
import java.util.Arrays;
public class WordCount {
public static void main(String[] args) {
SparkConf conf = new SparkConf()
.setAppName("WordCount")
.setMaster("local[*]");
JavaSparkContext sc = new JavaSparkContext(conf);
// 1. 读取文本文件
JavaRDD<String> lines = sc.textFile("data/sample.txt");
// 2. 拆分单词
JavaRDD<String> words = lines.flatMap(
line -> Arrays.asList(line.split("\\s+")).iterator()
);
// 3. 映射为 (word, 1) 的 PairRDD
JavaPairRDD<String, Integer> pairs = words.mapToPair(
word -> new Tuple2<>(word, 1)
);
// 4. 按 word 聚合计数
JavaPairRDD<String, Integer> counts = pairs.reduceByKey(
(a, b) -> a + b
);
// 5. 输出结果
counts.foreach(tuple -> System.out.println(tuple._1 + ": " + tuple._2));
// 6. 保存到文件
counts.saveAsTextFile("output/wordcount");
sc.stop();
}
}
4.5 窄依赖 vs 宽依赖(Shuffle)
窄依赖(Narrow Dependency) 宽依赖(Wide Dependency / Shuffle)
父分区 → 子分区 1:1 或 N:1 分区 → 子分区 M:N(需shuffle)
map / filter / union groupByKey / reduceByKey / join / repartition
不需要shuffle,可管道执行 需要shuffle,是Stage划分边界
性能要点:
-
尽量减少 shuffle 操作
-
reduceByKey比groupByKey更高效(在 map 端预聚合) -
shuffle 后数据会落盘,避免频繁 shuffle
4.6 RDD 缓存与持久化
// 缓存到内存(懒执行,下次action时才真正缓存)
rdd.cache(); // 等价于 persist(StorageLevel.MEMORY_ONLY)
// 持久化级别
rdd.persist(StorageLevel.MEMORY_AND_DISK); // 内存不足时落盘
rdd.persist(StorageLevel.MEMORY_ONLY_SER); // 序列化后存内存,省空间
rdd.persist(StorageLevel.DISK_ONLY); // 只存磁盘
// 手动释放缓存
rdd.unpersist();
| 级别 | 说明 | 适用场景 |
|---|---|---|
| MEMORY_ONLY | 直接存内存,不序列化 | 数据小、频繁使用 |
| MEMORY_AND_DISK | 内存+磁盘溢出 | 数据较大、仍需快速访问 |
| MEMORY_ONLY_SER | 序列化存内存 | 数据较大、CPU可接受序列化开销 |
| DISK_ONLY | 只存磁盘 | 数据量大、不频繁使用 |
4.7 PairRDD 常用操作
JavaPairRDD<String, Integer> pairRDD = ...;
// 聚合操作
pairRDD.reduceByKey((a, b) -> a + b); // 按Key求和
pairRDD.aggregateByKey(0, (a, b) -> a + b, (a, b) -> a + b); // 更灵活的聚合
pairRDD.foldByKey(0, (a, b) -> a + b); // 类似reduce但有零值
// 排序
pairRDD.sortByKey(); // 按 Key 升序
pairRDD.sortByKey(false); // 按 Key 降序
// 连接
pairRDD1.join(pairRDD2); // 内连接 → (K, (V1, V2))
pairRDD1.leftOuterJoin(pairRDD2); // 左外连接
pairRDD1.rightOuterJoin(pairRDD2); // 右外连接
pairRDD1.cogroup(pairRDD2); // 共分组 → (K, (Iterable<V1>, Iterable<V2>))
// 统计
pairRDD.countByKey(); // 返回 Map<K, Long>
pairRDD.countByValue(); // 返回 Map<(K,V), Long>
pairRDD.collectAsMap(); // 返回 Map<K, V>(注意:每个Key只保留一个Value)
dataframe和dataset:
DataFrame/Dataset 是 Spark 2+ 的推荐 API,比 RDD 更高效(有 Catalyst 优化器和 Tungsten 执行引擎)。
5.1 三种 API 对比
| 特性 | RDD | DataFrame | Dataset |
|---|---|---|---|
| 类型安全 | 编译期检查 | 无类型检查(Row) | 编译期检查(Java Bean) |
| 优化 | 无自动优化 | Catalyst + Tungsten | Catalyst + Tungsten |
| API 语言 | Java/Scala/Python | Java/Scala/Python/R | Scala/Java |
| 性能 | 较低 | 高 | 高(Spark3 编码优化后≈DataFrame) |
5.2 创建 DataFrame
从集合创建
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import java.util.Arrays;
import java.util.List;
SparkSession spark = SparkSession.builder()
.appName("DataFrameDemo")
.master("local[*]")
.getOrCreate();
// 方式1:从 Java Bean 反射推断 schema
List<Person> data = Arrays.asList(
new Person("Alice", 28),
new Person("Bob", 35),
new Person("Charlie", 22)
);
Dataset<Row> df = spark.createDataFrame(data, Person.class);
df.show();
df.printSchema();
// Person 类需要符合 Java Bean 规范
public class Person implements Serializable {
private String name;
private int age;
// 必须有无参构造器
public Person() {}
public Person(String name, int age) { this.name = name; this.age = age; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
// 方式2:手动指定 schema
import org.apache.spark.sql.types.*;
StructType schema = new StructType(new StructField[]{
DataTypes.createStructField("name", DataTypes.StringType, false),
DataTypes.createStructField("age", DataTypes.IntegerType, false)
});
List<Row> rowList = Arrays.asList(
RowFactory.create("Alice", 28),
RowFactory.create("Bob", 35)
);
Dataset<Row> df2 = spark.createDataFrame(rowList, schema);
从文件创建
// JSON 文件
Dataset<Row> jsonDf = spark.read().json("data/people.json");
// CSV 文件
Dataset<Row> csvDf = spark.read()
.option("header", "true")
.option("inferSchema", "true") // 自动推断类型
.csv("data/people.csv");
// Parquet 文件(列式存储,推荐生产使用)
Dataset<Row> parquetDf = spark.read().parquet("data/people.parquet");
// ORC 文件
Dataset<Row> orcDf = spark.read().orc("data/people.orc");
// JDBC 数据库
Dataset<Row> jdbcDf = spark.read()
.format("jdbc")
.option("url", "jdbc:mysql://localhost:3306/mydb")
.option("dbtable", "users")
.option("user", "root")
.option("password", "123456")
.load();
// Text 文件(每行一个字符串)
Dataset<Row> textDf = spark.read().textFile("data/logs.txt");
5.3 DataFrame 基本操作
DSL(领域特定语言)风格
Dataset<Row> df = spark.read().json("data/people.json");
// 查看
df.show(); // 展示数据
df.printSchema(); // 展示 schema
df.columns(); // 列名数组
df.count(); // 行数
df.describe().show(); // 统计摘要(count, mean, stddev, min, max)
// 选择 / 过滤 / 排序
df.select("name", "age").show();
df.select(col("name"), col("age").plus(1).as("age_plus_1")).show();
df.filter(col("age").gt(25)).show(); // age > 25
df.where("age > 25").show(); // 同 filter
df.orderBy(col("age").desc()).show();
df.sort("age").show();
// 聚合
df.groupBy("name").count().show();
df.groupBy("name").agg(
avg("age").as("avg_age"),
max("age").as("max_age"),
sum("age").as("sum_age")
).show();
// 去重
df.distinct().show();
df.dropDuplicates("name").show(); // 按指定列去重
// 添加 / 重命名列
df.withColumn("is_senior", col("age").gt(60)).show();
df.withColumnRenamed("name", "full_name").show();
// Join
Dataset<Row> df1 = ...;
Dataset<Row> df2 = ...;
df1.join(df2, df1.col("id").equalTo(df2.col("id")), "inner").show(); // inner join
df1.join(df2, df1.col("id").equalTo(df2.col("id")), "left").show(); // left join
import 提示:需要
import static org.apache.spark.sql.functions.*;来使用col、avg、max、sum等。
SQL 风格
// 注册临时视图
df.createOrReplaceTempView("people");
// 注册全局视图(跨 SparkSession 可见)
df.createGlobalTempView("global_people");
// 执行 SQL
Dataset<Row> result = spark.sql("SELECT name, age FROM people WHERE age > 25 ORDER BY age DESC");
result.show();
// 复杂 SQL 也支持
Dataset<Row> aggResult = spark.sql(
"SELECT name, AVG(age) as avg_age, COUNT(*) as cnt " +
"FROM people GROUP BY name HAVING cnt > 1"
);
5.4 Dataset(强类型 API)
import org.apache.spark.sql.Dataset;
// DataFrame → Dataset(需要有对应 Bean 类)
Dataset<Person> personDs = df.as(Encoders.bean(Person.class));
// Dataset → DataFrame
Dataset<Row> backToDf = personDs.toDF();
// Dataset 特有操作(类似 Stream API)
personDs.filter(p -> p.getAge() > 25); // lambda 过滤
personDs.map(p -> new Person(p.getName(), p.getAge() + 1), // lambda 映射
Encoders.bean(Person.class));
// 基础类型 Dataset
Dataset<Integer> intDs = spark.range(10).map(x -> (int) x.getAs(0), Encoders.INT());
Spark 3 重要改进:Dataset 的序列化开销已大幅优化(Spark 3 使用
Encoders.kryo/Encoders.javaSerialization的场景更少,Bean Encoder 性能接近 DataFrame)。
5.5 DataFrame 写出数据
// 写出为 Parquet(默认按 snappy 压缩)
df.write().parquet("output/people.parquet");
// 写出为 CSV
df.write()
.option("header", "true")
.mode("overwrite") // 覆盖模式
.csv("output/people.csv");
// 写出为 JSON
df.write().json("output/people.json");
// 写出到 JDBC
df.write()
.format("jdbc")
.option("url", "jdbc:mysql://localhost:3306/mydb")
.option("dbtable", "users_output")
.option("user", "root")
.option("password", "123456")
.mode("append") // 追加模式
.save();
// 写出模式
// SaveMode.Overwrite → 覆盖已有数据
// SaveMode.Append → 追加
// SaveMode.ErrorIfExists → 已存在则报错(默认)
// SaveMode.Ignore → 已存在则跳过
5.6 完整案例:CSV 数据清洗与分析
package com.example;
import org.apache.spark.sql.SparkSession;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import static org.apache.spark.sql.functions.*;
public class CsvAnalysis {
public static void main(String[] args) {
SparkSession spark = SparkSession.builder()
.appName("CsvAnalysis")
.master("local[*]")
.getOrCreate();
// 1. 读取 CSV(自动推断类型)
Dataset<Row> sales = spark.read()
.option("header", "true")
.option("inferSchema", "true")
.csv("data/sales.csv"); // 字段: date, product, category, amount, quantity
// 2. 数据清洗
Dataset<Row> cleaned = sales
.filter(col("amount").isNotNull()
.and(col("quantity").gt(0)))
.withColumn("amount", round(col("amount"), 2)) // 金额保留2位小数
.withColumn("month", substring(col("date"), 1, 7)); // 提取月份
// 3. 分品类月度汇总
Dataset<Row> monthly = cleaned.groupBy("category", "month")
.agg(
sum("amount").as("total_amount"),
sum("quantity").as("total_qty"),
avg("amount").as("avg_amount"),
count("*").as("order_count")
)
.orderBy("category", "month");
monthly.show(50);
// 4. 写出结果
monthly.write()
.mode("overwrite")
.option("header", "true")
.csv("output/monthly_summary");
spark.stop();
}
}
在dataworks中的spark程序实战(基于java8)
1.简单df验证
创建空白的maven程序,引入官方样板工程的依赖:https://github.com/aliyun/MaxCompute-Spark
(可以适当精简依赖和插件,比如scala插件可以注释掉)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>my-spark-demo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>my-spark-demo</name> <url>http://maven.apache.org</url> <properties> <spark.version>2.3.0</spark.version> <cupid.sdk.version>3.3.8-public</cupid.sdk.version> <scala.version>2.11.8</scala.version> <scala.binary.version>2.11</scala.binary.version> <java.version>1.8</java.version> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_${scala.binary.version}</artifactId> <version>${spark.version}</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> </exclusion> <exclusion> <groupId>org.scala-lang</groupId> <artifactId>scalap</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_${scala.binary.version}</artifactId> <version>${spark.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-mllib_${scala.binary.version}</artifactId> <version>${spark.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming_${scala.binary.version}</artifactId> <version>${spark.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.aliyun.odps</groupId> <artifactId>cupid-sdk</artifactId> <version>${cupid.sdk.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.aliyun.odps</groupId> <artifactId>odps-spark-datasource_${scala.binary.version}</artifactId> <version>${cupid.sdk.version}</version> </dependency> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> <version>${scala.version}</version> </dependency> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-actors</artifactId> <version>${scala.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.4.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <minimizeJar>false</minimizeJar> <shadedArtifactAttached>true</shadedArtifactAttached> <artifactSet> <includes> <!-- Include here the dependencies you want to be packed in your fat jar --> <include>*:*</include> </includes> </artifactSet> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> <exclude>**/log4j.properties</exclude> </excludes> </filter> </filters> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>reference.conf</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource> META-INF/services/org.apache.spark.sql.sources.DataSourceRegister </resource> </transformer> </transformers> </configuration> </execution> </executions> </plugin> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <version>3.3.2</version> <executions> <execution> <id>scala-compile-first</id> <phase>process-resources</phase> <goals> <goal>compile</goal> </goals> </execution> <execution> <id>scala-test-compile-first</id> <phase>process-test-resources</phase> <goals> <goal>testCompile</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>my-spark-demo</artifactId> <version>2.0-SNAPSHOT</version> <packaging>jar</packaging> <name>my-spark-demo</name> <url>http://maven.apache.org</url> <properties> <spark.version>2.3.0</spark.version> <cupid.sdk.version>3.3.8-public</cupid.sdk.version> <scala.version>2.11.8</scala.version> <scala.binary.version>2.11</scala.binary.version> <java.version>1.8</java.version> <maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.target>${java.version}</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_${scala.binary.version}</artifactId> <version>${spark.version}</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> </exclusion> <exclusion> <groupId>org.scala-lang</groupId> <artifactId>scalap</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_${scala.binary.version}</artifactId> <version>${spark.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-mllib_${scala.binary.version}</artifactId> <version>${spark.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming_${scala.binary.version}</artifactId> <version>${spark.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.aliyun.odps</groupId> <artifactId>cupid-sdk</artifactId> <version>${cupid.sdk.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.aliyun.odps</groupId> <artifactId>odps-spark-datasource_${scala.binary.version}</artifactId> <version>${cupid.sdk.version}</version> </dependency> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-library</artifactId> <version>${scala.version}</version> </dependency> <dependency> <groupId>org.scala-lang</groupId> <artifactId>scala-actors</artifactId> <version>${scala.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.4.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <minimizeJar>false</minimizeJar> <shadedArtifactAttached>true</shadedArtifactAttached> <artifactSet> <includes> <!-- Include here the dependencies you want to be packed in your fat jar --> <include>*:*</include> </includes> </artifactSet> <filters> <filter> <artifact>*:*</artifact> <excludes> <exclude>META-INF/*.SF</exclude> <exclude>META-INF/*.DSA</exclude> <exclude>META-INF/*.RSA</exclude> <exclude>**/log4j.properties</exclude> </excludes> </filter> </filters> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>reference.conf</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource> META-INF/services/org.apache.spark.sql.sources.DataSourceRegister </resource> </transformer> </transformers> </configuration> </execution> </executions> </plugin> <plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <version>3.3.2</version> <executions> <execution> <id>scala-compile-first</id> <phase>process-resources</phase> <goals> <goal>compile</goal> </goals> </execution> <execution> <id>scala-test-compile-first</id> <phase>process-test-resources</phase> <goals> <goal>testCompile</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
主程序:
package org.example.app; import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; import org.apache.spark.sql.SparkSession; import org.example.bean.Person; import java.util.Arrays; import java.util.List; /** * Hello world! * */ public class App { public static void main( String[] args ) { SparkSession spark = SparkSession.builder() .appName("HelloSpark") .getOrCreate(); System.out.println("Spark version: " + spark.version()); List<Person> personList = Arrays.asList( new Person("Alice", 1), new Person("Bob", 2), new Person("Cathy", 3) ); Dataset<Row> df = spark.createDataFrame(personList, Person.class); df.show(); spark.stop(); } }
java bean:
package org.example.bean; import java.io.Serializable; public class Person implements Serializable { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
记得设置正确的JDK:

项目依赖也要设置:

参考文档进行发布:https://help.aliyun.com/zh/maxcompute/user-guide/using-dataworks-spark?spm=a2c4g.11186623.help-menu-27797.d_2_2_1_0_1.6407141cCYPiz5&scm=20140722.H_3019626._.OR_help-T_cn~zh-V_1
如果是新任务,记得添加kube和cupid相关配置:https://help.aliyun.com/zh/maxcompute/user-guide/common-configuration?spm=a2c4g.11186623.help-menu-27797.d_2_2_1_2_0.51315ab23anTxI&scm=20140722.H_2881220._.OR_help-T_cn~zh-V_1
spark.hadoop.odps.kube.mode=true spark.hadoop.odps.cupid.data.proxy.enable=true
记得选择非公共资源组:

package org.example.app; import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; import org.apache.spark.sql.SparkSession; /** * Hello world! * */ public class App { public static void main( String[] args ) { // 1. 解析分区时间参数 if (args.length < 1) { System.err.println("Usage: DwdFullDataEWSAliyunSmartEu <pt>"); System.err.println(" pt: partition date (format: yyyyMMdd)"); System.exit(1); } String pt = args[0]; SparkSession spark = SparkSession.builder() .appName("HelloSpark") .getOrCreate(); // 读取普通表 String sql = "SELECT\n" + " vin,\n" + " client_version,\n" + " install_status,\n" + " model_year,\n" + " structure_week,\n" + " factory_complete_date,\n" + " collect_function,\n" + " data_collection,\n" + " remote_diagnostics,\n" + " market,\n" + " ota_backend_function,\n" + " rvdc_backend_function,\n" + " portstarttimestamp,\n" + " etltime,\n" + " dt\n" + " FROM data_act_overseas_fk_test.dwd_ota_vehicle_info_di\n" + " WHERE dt = '" + pt + "' "; Dataset<Row> ota_vehicle_info_df = spark.sql(sql); System.out.println("ota_vehicle_info_df count: " + ota_vehicle_info_df.count()); ota_vehicle_info_df.show(); spark.stop(); } }
配置:(记得资源组不要用公共资源组)

自定义函数:
1)实现UDF:
Spark 提供了 UDF0 到 UDF22,数字代表输入参数个数。每个 UDF 必须指定返回类型。
import org.apache.spark.sql.api.java.UDF1; import org.apache.spark.sql.api.java.UDF2; import org.apache.spark.sql.types.DataType; import org.apache.spark.sql.types.DataTypes; // 单参数 UDF:给字符串加前缀 public class PrefixUDF implements UDF1<String, String> { @Override public String call(String input) throws Exception { if (input == null) return null; return "PREFIX_" + input; } } // 双参数 UDF:根据 age 和 threshold 判断是否成年 public class IsAdultUDF implements UDF2<Integer, Integer, Boolean> { @Override public Boolean call(Integer age, Integer threshold) throws Exception { if (age == null) return null; return age >= threshold; } } // 类型转换 UDF:Long → 格式化日期字符串 public class FormatDateUDF implements UDF1<Long, String> { @Override public String call(Long timestamp) throws Exception { if (timestamp == null) return null; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); return sdf.format(new Date(timestamp)); } }
2)注册UDF:
SparkSession spark = SparkSession.builder() .appName("UDF Demo") .getOrCreate(); // 方式一:注册实例(Spark 2 & 3 都支持) spark.udf().register("add_prefix", new PrefixUDF(), DataTypes.StringType); spark.udf().register("is_adult", new IsAdultUDF(), DataTypes.BooleanType); spark.udf().register("format_date", new FormatDateUDF(), DataTypes.StringType); // 方式二:Java 8 Lambda(更简洁,但需要显式指定返回类型) spark.udf().register("upper_case", (UDF1<String, String>) s -> s == null ? null : s.toUpperCase(), DataTypes.StringType); // 方式三:Spark 3 新增 — registerJavaFunction(从已有类名注册) // 适合无法直接实例化的场景(如类在别的 jar 中) spark.udf().registerJavaFunction("add_prefix_2", "com.example.PrefixUDF", DataTypes.StringType);
使用UDF:
// SQL 方式 spark.sql("SELECT add_prefix(name) AS prefixed_name FROM users"); spark.sql("SELECT is_adult(age, 18) AS is_adult_flag FROM users"); spark.sql("SELECT format_date(ts) AS date_str FROM events"); // DataFrame API 方式 import org.apache.spark.sql.functions; Dataset<Row> df = spark.table("users"); // 通过 callUDF 调用 df.select( functions.callUDF("add_prefix", functions.col("name")), functions.callUDF("is_adult", functions.col("age"), functions.lit(18)) ); // 更推荐:先注册再用 withColumn df.withColumn("prefixed_name", functions.callUDF("add_prefix", functions.col("name"))) .withColumn("is_adult", functions.callUDF("is_adult", functions.col("age"), functions.lit(18))) .show();
小视图可以直接存内容,不用落表:
// 持久化到内存
fa_df.persist(StorageLevel.MEMORY_ONLY());
fa_df.createOrReplaceTempView("fa_view");

浙公网安备 33010602011771号