《零基础入门Spark》学习笔记 Day 06

数据源与数据格式

一、Driver端创建DataFrame

首先用Driver端数据结构创建RDD,然后再调用createDataFrame把RDD转化为DataFrame

对于大规模数据,优先使用并行集合(RDD)而非本地集合。在创建DataFrame前,确保数据已在Driver端内存中可管理。对于复杂数据结构,预先定义明确的schema能提高处理效率。

当从Pandas DataFrame转换时,注意数据大小,因为所有数据需要先加载到Driver内存中。大数据集应考虑分块处理或直接使用Spark数据源API读取。

二、文件系统创建DataFrame

Spark支持多种文件系统,常见的有HDFS、Amazon S3、本地文件系统等。无论哪种文件系统,Spark都要把通过SparkSession的read API来读取数据并创建DataFrame。

下面是千问的示例代码

java 复制代码
 // 初始化 SparkSession
        SparkSession spark = SparkSession.builder()
            .appName("FileSystemDataFrameDemo")
            .master("local[*]")
            .getOrCreate();

        // 设置日志级别(可选)
        spark.sparkContext().setLogLevel("WARN");

        try {
            // 示例1:从 CSV 文件读取
            Dataset<Row> csvDf = spark.read()
                .option("header", "true")
                .option("inferSchema", "true")
                .csv("data/users.csv");

            System.out.println("=== CSV DataFrame ===");
            csvDf.show();
            csvDf.printSchema();

            // 示例2:从 JSON 文件读取
            Dataset<Row> jsonDf = spark.read()
                .option("multiline", "true")
                .json("data/users.json");

            System.out.println("=== JSON DataFrame ===");
            jsonDf.show();

            // 示例3:从 Parquet 文件读取
            Dataset<Row> parquetDf = spark.read()
                .parquet("data/users.parquet");

            System.out.println("=== Parquet DataFrame ===");
            parquetDf.show();

            // 基本操作示例
            System.out.println("=== 基本操作 ===");
            csvDf.filter(csvDf.col("age").gt(25)).show();
            csvDf.groupBy("city").count().show();
            csvDf.createOrReplaceTempView("users");
            spark.sql("SELECT city, COUNT(*) as cnt FROM users GROUP BY city").show();

            // 写入文件示例
            csvDf.write()
                .mode(SaveMode.Overwrite)
                .parquet("output/users_parquet");

            csvDf.write()
                .option("header", "true")
                .mode(SaveMode.Overwrite)
                .csv("output/users_csv");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            spark.stop();
        }

三、CSV创建DataFrame

常见的读取选项

选项 说明 示例值
header 是否包含表头 true/false
inferSchema 是否自动推断字段类型 true/false
delimiter 分隔符 , / \t / |
encoding 文件编码 UTF-8 / GBK
mode 错误处理模式 PERMISSIVE / DROPMALFORMED / FAILFAST
nullValue 空值表示 NULL / \N
escape 转义字符 \
quote 引号字符 "

四、Parquest/ORC创建DataFrame

ORC常见读取/写入选项

选项 说明 示例值
compression 压缩方式 NONE / SNAPPY / ZLIB / LZO
mergeSchema 合并多个文件的 schema true/false
filterPushdown 启用谓词下推优化 true/false
ignoreCorruptFiles 忽略损坏文件 true/false
orc.create.index 创建索引 true/false
orc.stripe.size 条带大小(字节) 67108864(默认 64MB)
orc.row.index.stride 行索引步长 10000
orc.bloom.filter.columns 布隆过滤器列 col1,col2
orc.bloom.filter.fpp 布隆过滤器误判率 0.0001

Parquest常见读取/写入选项

选项 说明 示例值
compression 压缩方式 snappy / gzip / lz4 / zstd / uncompressed
mergeSchema 合并多个文件的 schema true/false
filterPushdown 启用谓词下推优化 true/false
ignoreCorruptFiles 忽略损坏文件 true/false
recursiveFileLookup 递归查找子目录 true/false
parquet.enable.dictionary 启用字典编码 true/false
parquet.writer.version Parquet 写入版本 v1 / v2
parquet.block.size 块大小(字节) 134217728(默认 128MB)
parquet.row.group.size 行组大小(字节) 134217728(默认 128MB)

五、RDBMS创建DataFrame

以MYSQL为例,下面是千问的示例代码

java 复制代码
SparkSession spark = SparkSession.builder()
            .appName("JdbcToDataFrameDemo")
            .master("local[*]")
            .getOrCreate();

        spark.sparkContext().setLogLevel("WARN");

        // 配置 JDBC 连接属性
        Properties connectionProperties = new Properties();
        connectionProperties.put("user", USER);
        connectionProperties.put("password", PASSWORD);
        connectionProperties.put("driver", DRIVER);
        // 优化:设置 fetchsize 避免内存溢出
        connectionProperties.put("fetchSize", "1000"); 

        try {
            // === 1. 读取数据 ===

            // 方法 1:直接读取整张表
            System.out.println("=== 方法 1:读取整张表 ===");
            Dataset<Row> df1 = spark.read()
                .jdbc(URL, TABLE, connectionProperties);
            df1.show();
            df1.printSchema();

            // 方法 2:读取查询结果(子查询)
            System.out.println("=== 方法 2:读取查询结果 ===");
            String subQuery = "(SELECT id, name, salary FROM " + TABLE + " WHERE salary > 15000) AS high_salary_emp";
            Dataset<Row> df2 = spark.read()
                .jdbc(URL, subQuery, connectionProperties);
            df2.show();

            // 方法 3:分区读取(推荐用于大表,提高并行度)
            System.out.println("=== 方法 3:分区读取 ===");
            Dataset<Row> df3 = spark.read()
                .jdbc(URL, TABLE, "id", 1, 100, 4, connectionProperties);
                // 参数说明:column, lowerBound, upperBound, numPartitions
            System.out.println("分区数:" + df3.rdd().getNumPartitions());
            df3.show();

            // 方法 4:使用 DataFrameReader 选项配置
            System.out.println("=== 方法 4:选项配置读取 ===");
            Dataset<Row> df4 = spark.read()
                .format("jdbc")
                .option("url", URL)
                .option("dbtable", TABLE)
                .option("user", USER)
                .option("password", PASSWORD)
                .option("driver", DRIVER)
                .option("fetchSize", "1000")
                .option("partitionColumn", "id")
                .option("lowerBound", "1")
                .option("upperBound", "100")
                .option("numPartitions", "4")
                .load();
            df4.show();

            // === 2. 数据处理 ===
            System.out.println("=== 数据处理 ===");
            df4.filter(df4.col("age").gt(30))
               .groupBy("department")
               .agg(
                   org.apache.spark.sql.functions.avg("salary").alias("avg_salary"),
                   org.apache.spark.sql.functions.count("id").alias("count")
               )
               .show();

            // === 3. 写入数据回数据库 ===
            
            // 准备写入的数据
            Dataset<Row> newData = spark.createDataFrame(
                java.util.Arrays.asList(
                    new org.apache.spark.sql.Row[]{
                        org.apache.spark.sql.RowFactory.create(9, "新员工", 25, "技术部", 14000.00, java.sql.Date.valueOf("2023-01-01"))
                    }
                ),
                df4.schema()
            );

            // 写入模式:Append, Overwrite, Ignore, ErrorIfExists
            System.out.println("=== 写入数据库 (Append) ===");
            newData.write()
                .mode(SaveMode.Append)
                .jdbc(URL, TABLE, connectionProperties);

            // 批量写入优化
            System.out.println("=== 写入数据库 (带批量配置) ===");
            newData.write()
                .mode(SaveMode.Append)
                .option("batchsize", "1000")      // 每次提交记录数
                .option("isolationLevel", "NONE") // 事务隔离级别
                .option("numPartitions", "1")     // 写入并发度
                .jdbc(URL, TABLE + "_backup", connectionProperties); // 写入新表

            // === 4. 统计信息 ===
            System.out.println("=== 统计信息 ===");
            System.out.println("总行数:" + df4.count());

        } catch (Exception e) {
            System.err.println("发生错误:" + e.getMessage());
            e.printStackTrace();
        } finally {
            spark.stop();
        }
相关推荐
什巳15 分钟前
JAVA练习278- 和为 K 的子数组
java·学习·算法·leetcode
碎光拾影1 小时前
CPU与MCU核心区别揭秘
单片机·学习·51单片机
栈溢出了2 小时前
Redis 分片集群与哈希槽笔记
java·redis·笔记·spring
我命由我123453 小时前
复利极简理解
经验分享·笔记·学习·职场和发展·求职招聘·职场发展·学习方法
AOwhisky3 小时前
Python 学习笔记(第三期)——流程控制核心知识点自测与详解
开发语言·笔记·python·学习·云原生·运维开发·流程控制
tyqtyq223 小时前
药品说明书解读 —— AI 安全用药助手,鸿蒙原生应用深度解析
人工智能·学习·华为·生活·harmonyos
小心亦新4 小时前
STM32学习13 定时器1中断
stm32·嵌入式硬件·学习
我命由我123454 小时前
72 法则极简理解
经验分享·笔记·学习·职场和发展·求职招聘·职场发展·学习方法
qq_263_tohua5 小时前
第112期 CNN学习,不错的文章
学习
我想我不够好。5 小时前
满级的惩戒在范围
学习