You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PySpark保存500万行DataFrame时Java堆内存溢出问题求助

解决PySpark写入OOM的替代方案与优化建议

一、换用低内存占用的存储格式

既然Parquet写入触发内存溢出,试试这些兼容性强、内存压力更小的格式:

  • CSV格式:写入时限制单文件记录数,避免大文件拖垮内存,其他脚本用pandas/PySpark都能直接读:
    stream_df.write.mode("overwrite").option("maxRecordsPerFile", 100000).csv("./Stream3_csv")
    
  • JSON格式:同样支持分块写入,结构化数据读取无压力:
    stream_df.write.mode("overwrite").option("maxRecordsPerFile", 80000).json("./Stream3_json")
    
  • ORC格式:列式存储但内存占用比Parquet略低,稳定性更好:
    stream_df.write.mode("overwrite").orc("./Stream3_orc")
    

二、优化数据处理流程,从根源减内存开销

你拆分时间后再关联主表的操作会产生大量shuffle,试试直接拆分合并,跳过关联步骤:

# 直接拆分出开始事件记录
start_df = df.select(
    col("ID"),
    (col("Start_Time").cast("timestamp").cast("long") / RUNTIME).alias("Stream_Time"),
    lit("START").alias("Event_Type"),
    # 只选后续脚本需要的列,别带全量字段
    col("Severity"), col("Weather_Condition"), col("State")
)

# 拆分出结束事件记录
end_df = df.select(
    col("ID"),
    (col("End_Time").cast("timestamp").cast("long") / RUNTIME).alias("Stream_Time"),
    lit("END").alias("Event_Type"),
    col("Severity"), col("Weather_Condition"), col("State")
)

# 合并两个DataFrame,直接得到最终的stream_df,不用再关联
stream_df = start_df.union(end_df)

这样省掉了ID关联的shuffle操作,内存占用会大幅降低。另外记得只保留必要字段,别把没用的列都带上。

三、调整Spark配置的关键参数

除了spark.driver.memory,这些参数能帮你避开OOM:

from pyspark.sql import SparkSession
spark = SparkSession.builder \
    .appName("AccidentStream") \
    .config("spark.driver.memory", "8g")  # 别设太大,避免终端/Jupyter崩溃
    .config("spark.executor.memory", "6g") \
    .config("spark.driver.maxResultSize", "4g")  # 限制Driver端结果的最大尺寸
    .config("spark.sql.shuffle.partitions", "150")  # 根据数据量调shuffle分区数,默认200可适当减
    .getOrCreate()

# 开启内存溢出时自动写磁盘
spark.conf.set("spark.storage.memoryFraction", "0.5")
spark.conf.set("spark.sql.inMemoryColumnarStorage.compressed", "true")

四、分批次写入数据

如果数据量实在太大,一次性写入风险高,就分批次处理:

# 获取所有唯一ID,分批处理
id_list = df.select("ID").distinct().rdd.map(lambda x: x[0]).collect()
batch_size = 10000  # 每批处理10000个ID

for idx in range(0, len(id_list), batch_size):
    batch_ids = id_list[idx:idx+batch_size]
    # 过滤当前批次的数据
    batch_data = df.filter(col("ID").isin(batch_ids))
    # 执行拆分逻辑
    start_batch = batch_data.select(...)
    end_batch = batch_data.select(...)
    stream_batch = start_batch.union(end_batch)
    # 追加写入到目标路径
    stream_batch.write.mode("append").csv("./Stream3_csv_batched")

内容的提问来源于stack exchange,提问作者Mohamed Yasser

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 22:54:21