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.部署方式:

image

 部署命令示例:

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

 

RDD 是 Spark 最底层的抽象,Spark 2/3 均保留。日常开发优先用 DataFrame,但理解 RDD 有助于掌握底层机制。

4.1 RDD 核心属性

  1. 分区列表:数据被切成多个 Partition

  2. 计算函数:每个 Partition 如何计算(compute

  3. 依赖列表:对其他 RDD 的依赖关系(窄依赖 / 宽依赖)

  4. 分区器:可选,定义数据如何分区(如 HashPartitioner)

  5. 首选位置:可选,分区优先计算的位置(如 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 操作

  • reduceByKeygroupByKey 更高效(在 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 对比

特性RDDDataFrameDataset
类型安全 编译期检查 无类型检查(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.*; 来使用 colavgmaxsum 等。

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>
View Code
<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>
View Code

 

主程序:

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();
    }
}
View Code

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;
    }
}
View Code

 


记得设置正确的JDK:

image

 项目依赖也要设置:

image

 参考文档进行发布: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

记得选择非公共资源组:

image

 

 2.读取mc表
程序:
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();
    }
}

配置:(记得资源组不要用公共资源组)

image

 

自定义函数:

1)实现UDF:

Spark 提供了 UDF0UDF22,数字代表输入参数个数。每个 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");
posted @ 2026-06-10 16:21  ---江北  阅读(7)  评论(0)    收藏  举报
TOP