Spark 数据源:高效读写多种格式
Spark 提供了统一的数据源 API,支持读写多种数据格式,包括列式存储(Parquet/ORC)、文本格式(JSON/CSV)、二进制格式(Avro)以及关系型数据库(JDBC)等。重点掌握 Parquet 和 ORC 这两种高效的列式存储格式。
一、核心数据源概览
格式 | 类型 | 特点 | 适用场景 |
---|---|---|---|
Parquet | 列式 | Spark 默认格式,高效压缩,支持谓词下推 | 大数据分析首选 |
ORC | 列式 | Hive 优化格式,ACID 支持,高效压缩 | Hive 生态系统 |
JSON | 行式 | 易读,支持复杂结构 | 半结构化数据交换 |
CSV | 行式 | 通用表格格式 | 表格数据交换 |
Avro | 行式 | 二进制,Schema 演化支持 | 数据序列化 |
JDBC | 关系型 | 连接传统数据库 | 数据库集成 |
Hive | 列式/行式 | 数据仓库集成 | 数仓分析 |
二、列式存储重点:Parquet & ORC
1. Parquet - Spark 默认存储格式
特点:
- 列式存储:只读取需要的列
- 高效压缩:同列数据类型一致,压缩率高
- 谓词下推:过滤条件下推到存储层
- Schema 演化:支持添加/重命名列
读写操作:
# 读取 Parquet
df = spark.read.parquet("hdfs:///data/parquet/")
# 写入 Parquet(推荐 Snappy 压缩)
(df.write
.option("compression", "snappy")
.parquet("hdfs:///output/parquet/"))
# 分区写入(提高查询性能)
(df.write
.partitionBy("year", "month")
.parquet("hdfs:///partitioned_data/"))
2. ORC - Hive 优化格式
特点:
- 更高压缩率(相比 Parquet)
- 内置索引(轻量级索引)
- ACID 事务支持(Hive 3+)
- 向量化读取优化
读写操作:
# 读取 ORC
df = spark.read.orc("hdfs:///data/orc/")
# 写入 ORC(推荐 Zlib 压缩)
(df.write
.option("compression", "zlib")
.orc("hdfs:///output/orc/"))
# 分桶写入(优化 JOIN 性能)
(df.write
.bucketBy(128, "user_id")
.sortBy("timestamp")
.saveAsTable("bucketed_table"))
3. 列式存储最佳实践
- 分区策略:按时间(年/月/日)或类别分区
- 压缩选择:
- Parquet:Snappy(平衡速度/压缩率)
- ORC:Zlib(高压缩率)
- 列裁剪:只选择需要的列
- 谓词下推:在读取时应用过滤条件
spark.read.parquet("...").filter("age > 30") # 自动下推
三、其他数据源操作
1. JSON(半结构化数据)
# 读取 JSON
df_json = spark.read.json("data.json")
# 多行 JSON
spark.read.option("multiLine", True).json("complex.json")
# 写入 JSON(不推荐大数据集)
df.write.json("output.json")
2. CSV(表格数据)
# 读取 CSV
df_csv = (spark.read
.option("header", True)
.option("delimiter", ";")
.option("inferSchema", True) # 谨慎使用
.csv("data.csv"))
# 指定 Schema 读取(推荐)
from pyspark.sql.types import *
schema = StructType([
StructField("name", StringType()),
StructField("age", IntegerType())
])
df_safe = spark.read.schema(schema).csv("data.csv")
# 写入 CSV
df.write.option("header", True).csv("output.csv")
3. JDBC(关系数据库)
# 读取 PostgreSQL
jdbc_df = (spark.read
.format("jdbc")
.option("url", "jdbc:postgresql://localhost/mydb")
.option("dbtable", "employees")
.option("user", "user")
.option("password", "pass")
.option("fetchsize", "10000") # 分批读取
.load())
# 写入 MySQL(覆盖模式)
(df.write
.format("jdbc")
.option("url", "jdbc:mysql://localhost/test")
.option("dbtable", "new_data")
.option("user", "user")
.option("password", "pass")
.mode("overwrite")
.save())
4. Hive 集成
# 启用 Hive 支持
spark = SparkSession.builder.appName("HiveExample").enableHiveSupport().getOrCreate()
# 读取 Hive 表
hive_df = spark.sql("SELECT * FROM my_hive_table")
# 写入 Hive 表
df.write.saveAsTable("new_hive_table") # 内部表
# 外部表(保留数据文件)
df.write.option("path", "/hdfs/path").saveAsTable("external_table")
5. Avro(二进制序列化)
# 需要添加包:--packages org.apache.spark:spark-avro_2.12:3.3.0
df_avro = spark.read.format("avro").load("data.avro")
df.write.format("avro").save("output.avro")
四、高级技巧与优化
1. 数据源混合读写
# 从 CSV 读取,转换后写入 Parquet
(spark.read.csv("input.csv")
.filter("value > 100")
.write.parquet("output.parquet"))
2. 并行读取数据库
# 分区读取大表
jdbc_df = (spark.read
.format("jdbc")
.option("url", "jdbc:postgresql://localhost/db")
.option("dbtable", "big_table")
.option("user", "user")
.option("password", "pass")
.option("numPartitions", "10") # 并行度
.option("partitionColumn", "id")
.option("lowerBound", "1")
.option("upperBound", "1000000")
.load())
3. Schema 合并(Parquet)
# 启用 Schema 合并
spark.conf.set("spark.sql.parquet.mergeSchema", "true")
# 读取多个 Parquet 文件(不同 Schema)
merged_df = spark.read.parquet("data/year=2022/*", "data/year=2023/*")
4. 数据湖格式支持
# Delta Lake(ACID 事务支持)
df.write.format("delta").save("/delta/table")
# Iceberg 表
df.writeTo("catalog.db.table").using("iceberg").create()
五、数据源选择指南
场景 | 推荐格式 | 原因 |
---|---|---|
大数据分析 | Parquet | Spark 默认优化,高效压缩 |
Hive 集成 | ORC | Hive 原生支持,ACID 特性 |
数据交换 | JSON/CSV | 通用性强,易读 |
实时采集 | Avro | Schema 演化支持 |
数据库同步 | JDBC | 直接连接关系数据库 |
数仓建设 | Hive/Delta | 支持 ACID,版本控制 |
六、性能优化总结
- 列式存储优先:Parquet/ORC 比行式格式快 10-100 倍
- 分区分桶:加速数据裁剪和 JOIN 操作
- 压缩选择:
- 速度优先:Snappy
- 空间优先:Zlib/Gzip
- 谓词下推:在读取时过滤数据
- 列裁剪:只读取需要的列
- 并行读取:数据库大表使用分区读取
七、完整示例:端到端 ETL
# 1. 从 JDBC 读取
source_df = (spark.read
.format("jdbc")
.option("url", "jdbc:mysql://prod-db/sales")
.option("dbtable", "transactions")
.option("user", "etl_user")
.option("password", "secure_pass")
.load())
# 2. 数据处理
processed_df = (source_df
.filter("amount > 100")
.withColumn("date", to_date("timestamp"))
.select("id", "product_id", "amount", "date"))
# 3. 分区写入 Parquet
(processed_df.write
.partitionBy("date")
.option("compression", "snappy")
.parquet("hdfs:///data/sales/parquet/"))
# 4. 注册为 Hive 表
spark.sql("""
CREATE EXTERNAL TABLE sales_data (
id BIGINT,
product_id INT,
amount DECIMAL(10,2)
)
PARTITIONED BY (date DATE)
STORED AS PARQUET
LOCATION 'hdfs:///data/sales/parquet/'
""")
总结
Spark 数据源核心要点:
- 首选列式存储:Parquet 为默认选择,ORC 用于 Hive 集成
- 统一 API 接口:
spark.read
和dataframe.write
支持所有格式 - 优化关键:分区、压缩、谓词下推、列裁剪
- 格式转换:轻松实现数据管道(如 CSV → Parquet → Hive)
- 生态集成:无缝连接数据库、数据湖和数据仓库
掌握这些数据源操作技巧,能够大幅提升 Spark 数据处理的效率和可靠性,构建高性能的数据管道。