《零基础入门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();
        }
相关推荐
星恒随风6 小时前
MySQL 从零入门(五):MySQL 内置函数详解——日期、字符串、数学函数与实战案例
linux·数据库·笔记·sql·mysql
§ꦿCFོ༉7 小时前
嵌入式八股文—C语言入门学习(四)
c语言·开发语言·学习
2603_969694859 小时前
【JavaWeb学习记录】MyBatis核心知识点与实战笔记:从入门到动态SQL
笔记·sql·学习·mysql·mybatis·javaweb·学习方法
可乐ea10 小时前
智能体记忆该拆成几层:笔记、记忆表示与注入上下文
服务器·数据库·笔记·ai智能体·提示词工程·上下文工程·记忆系统
Go Work13 小时前
两个等值条件摆着,MySQL 偏要全表扫
经验分享·笔记
Oll Correct13 小时前
Adobe illustrator 案例十四:花元素标志草图绘制
笔记·ui·adobe·illustrator
北京云帆互联科技15 小时前
云帆培训考试系统更新说明:v9.3.0
java·学习
sxlishaobin17 小时前
Maven Setting详解
java·学习·maven
每月一号准时摆烂18 小时前
服务器运维笔记(一)——使用shell编写实时查看BMC功率的脚本
运维·服务器·笔记
柳鲲鹏2 天前
内斗学概论:43、平级内斗
笔记