【MapSheep】
[好记性不如烂笔头]

一、 核心认知(一句话总结)

Spark SQL 是 Spark 用于处理结构化数据的模块,支持 SQL 查询和 DataFrame/Dataset API,Java 操作 Spark SQL 本质是通过 Spark 提供的 Java 客户端 API,加载结构化数据、构建 DataFrame/Dataset、执行 SQL 或 DSL 操作,最终输出结果,适合大数据量的结构化数据处理(如日志分析、数仓ETL)。

二、 前置准备(必做步骤)

  1. 引入依赖(Maven 核心依赖,Spark 3.x 为例)
<dependencies>
    <!-- Spark SQL 核心依赖 -->
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-sql_2.12</artifactId>
        <version>3.4.0</version>
        <!-- 本地开发无需打包,集群运行需改为 provided -->
        <scope>compile</scope>
    </dependency>
</dependencies>
  1. 环境要求
  • JDK 8+(Spark 3.x 推荐 JDK 8/11)
  • 本地开发可无需部署 Spark 集群,使用本地模式(local[*]
  • 集群运行需将打包后的 Jar 包提交到 Spark 集群

三、 Java 操作 Spark SQL 核心流程(完整可运行示例)

核心步骤:创建 SparkSession → 加载数据构建 DataFrame → 注册临时视图 → 执行 SQL 查询 → 处理结果 → 关闭 SparkSession

import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;

public class JavaSparkSQLDemo {
    public static void main(String[] args) {
        // 1. 创建 SparkSession(Spark SQL 的入口,相当于 JDBC 的 Connection)
        SparkSession spark = SparkSession.builder()
                .appName("JavaSparkSQLDemo") // 应用名称
                .master("local[*]") // 本地模式,[*] 表示使用所有可用CPU核心,集群运行时删除该行
                .getOrCreate();

        try {
            // 2. 加载数据构建 DataFrame(两种常见方式:加载本地文件 / 构建内存数据)
            // 方式2.1:加载 CSV 文件(带表头)
            Dataset<Row> csvDF = spark.read()
                    .option("header", "true") // 第一行作为表头
                    .option("inferSchema", "true") // 自动推断字段类型
                    .csv("src/main/resources/user.csv"); // 本地 CSV 文件路径

            // 方式2.2:构建内存数据(演示用,无需外部文件)
            Dataset<Row> inMemoryDF = spark.createDataFrame(
                    // 封装数据(这里简化,实际可通过 JavaBean 构建)
                    java.util.Arrays.asList(
                            new User("张三", 25),
                            new User("李四", 30),
                            new User("王五", 28)
                    ), User.class
            );

            // 3. 注册临时视图(将 DataFrame 注册为 SQL 可查询的视图,类似数据库表)
            // 临时视图:仅当前 SparkSession 有效,关闭后失效
            inMemoryDF.createOrReplaceTempView("t_user");

            // 4. 执行 Spark SQL 查询(两种方式:纯 SQL / DSL 语法)
            // 方式4.1:执行纯 SQL 语句
            Dataset<Row> sqlResult = spark.sql("SELECT name, age FROM t_user WHERE age > 26");

            // 方式4.2:使用 DSL 语法(无需写 SQL,链式调用)
            Dataset<Row> dslResult = inMemoryDF.select("name", "age").where("age > 26");

            // 5. 处理并输出结果(show() 打印到控制台,实际可写入 HDFS/MySQL 等)
            System.out.println("=== SQL 查询结果 ===");
            sqlResult.show(); // 打印前20行数据

            System.out.println("=== DSL 查询结果 ===");
            dslResult.show();

            // 6. (可选)将结果写入外部存储(如 Parquet 文件)
            sqlResult.write()
                    .mode("overwrite") // 覆盖已有文件
                    .parquet("src/main/resources/result_parquet");

        } finally {
            // 7. 关闭 SparkSession,释放资源
            spark.close();
        }
    }

    // 定义 JavaBean 类,用于构建内存 DataFrame(字段需提供 getter/setter,序列化)
    public static class User {
        private String name;
        private int age;

        // 必须提供无参构造器(Spark 反射推断 schema 所需)
        public User() {}

        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }

        // getter 和 setter(必须提供,否则 Spark 无法读取字段)
        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; }
    }
}

补充说明

  1. 本地测试时,创建 user.csv 文件放在 src/main/resources/ 下,内容示例:
    name,age,gender
    张三,25,男
    李四,30,男
    王五,28,女
    
  2. 运行结果:控制台会打印年龄大于 26 的用户(李四、王五),同时生成 result_parquet 结果文件。

四、 关键注意点(有主见的核心提醒)

  1. SparkSession 是入口:所有 Spark SQL 操作都围绕 SparkSession 展开,相当于 JDBC 中的 Connection,无需再创建 SparkContext(Spark 3.x 中 SparkSession 已封装)。
  2. 临时视图 vs 全局临时视图
    • createOrReplaceTempView:仅当前 SparkSession 有效,适合单任务查询。
    • createGlobalTempView:跨 SparkSession 有效,视图名需带 global_temp. 前缀(如 SELECT * FROM global_temp.t_user),适合多任务共享数据。
  3. JavaBean 规范:用于构建 DataFrame 的 JavaBean 必须满足:提供无参构造器、所有字段有 getter/setter、实现序列化(可选,推荐),否则 Spark 无法反射推断字段类型。
  4. 本地模式 vs 集群模式:本地开发时用 master("local[*]"),提交到 Spark 集群时需删除该行,由集群的 spark-submit 命令指定 --master 参数。
  5. 数据写入模式write().mode() 支持 overwrite(覆盖)、append(追加)、ignore(忽略已存在)、errorIfExists(默认,存在则报错)。

五、 实际业务场景延伸

  1. 连接外部数据库(如 MySQL):引入 spark-sql-connector-mysql 依赖,通过 spark.read().jdbc() 加载数据,适合数仓同步。
  2. 自定义 UDF 函数:通过 spark.udf().register() 注册自定义函数,扩展 SQL 功能(如复杂数据处理)。
  3. 性能优化:避免全表扫描,使用 filter() 提前过滤数据,合理设置分区(repartition()),优先使用 Parquet 等列式存储格式。

总结

  1. Java 操作 Spark SQL 的核心是通过 SparkSession 完成「数据加载→视图注册→SQL/DSL 查询→结果输出」的流程。
  2. 前置依赖需引入 spark-sql_2.12,本地开发用 local[*] 模式,JavaBean 需满足无参构造器和 getter/setter 规范。
  3. 适合大数据结构化数据处理,集群运行时需删除本地模式配置,通过 spark-submit 提交 Jar 包。
posted on 2026-02-03 14:26  (Play)  阅读(11)  评论(0)    收藏  举报