数据源与数据格式
一、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();
}