PySpark 的分布式使用指南

140 阅读3分钟

PySpark 的分布式计算能力依赖于 集群管理框架(如 Spark Standalone、YARN、Mesos 或 Kubernetes)和 合理的代码设计。以下是详细的使用指南:


一、集群部署模式

PySpark 支持多种集群管理模式,配置方式如下:

模式适用场景启动命令示例
Local单机测试(非分布式)spark = SparkSession.builder.master("local[4]").getOrCreate()
Standalone专用 Spark 集群--master spark://master:7077
YARNHadoop 生态集成--master yarn --deploy-mode cluster
Kubernetes容器化部署--master k8s://https://<k8s-api-server>

二、分布式核心操作

1. 数据分区(关键优化点)

  • 默认分区:读取文件时自动按块分区(HDFS 默认 128MB/块)
  • 手动调整
    # 重新分区(触发Shuffle)
    df = df.repartition(100)  # 分成100个分区
    
    # 按列分区(避免Shuffle)
    df = df.write.partitionBy("date").parquet("output/")
    

2. 避免数据倾斜

# 方法1:加盐处理倾斜键
from pyspark.sql.functions import concat, lit, rand
df = df.withColumn("salted_key", concat(col("user_id"), lit("_"), (rand()*10).cast("int")))

# 方法2:两阶段聚合
df.groupBy("salted_key").agg(...).groupBy("user_id").agg(...)

3. 广播变量(Broadcast)

small_table = [...]  # 小数据集(<100MB)
broadcast_table = spark.sparkContext.broadcast(small_table)

# 在Executor中使用
def lookup(row):
    return broadcast_table.value.get(row["id"])
spark.udf.register("lookup_udf", lookup)

4. 累加器(Accumulator)

error_counter = spark.sparkContext.accumulator(0)

def validate(row):
    if row["value"] < 0:
        error_counter.add(1)
    return row

df.rdd.map(validate).count()
print("Errors:", error_counter.value)

三、资源调优参数

spark-submit 或 SparkSession 中配置:

参数说明推荐值(示例)
--executor-memory每个Executor内存4g(4GB)
--executor-cores每个Executor的CPU核数2
--num-executorsExecutor数量10
spark.sql.shuffle.partitionsShuffle分区数200(根据数据量调整)
spark.default.parallelism默认并行度executors * cores * 2

提交作业示例:

spark-submit \
  --master yarn \
  --executor-memory 8G \
  --num-executors 20 \
  my_script.py

四、代码示例:分布式ETL

场景:分布式清洗日志数据

from pyspark.sql import SparkSession
from pyspark.sql.functions import col, regexp_extract

# 初始化Spark(自动连接到集群)
spark = SparkSession.builder \
    .appName("DistributedETL") \
    .config("spark.sql.adaptive.enabled", "true") \  # 开启自适应优化
    .getOrCreate()

# 分布式读取数据(从HDFS/S3)
logs = spark.read.text("hdfs:///logs/*.log")

# 分布式处理(提取IP和访问时间)
cleaned = logs.select(
    regexp_extract(col("value"), r"(\d+\.\d+\.\d+\.\d+)", 1).alias("ip"),
    regexp_extract(col("value"), r"\[(.*?)\]", 1).alias("timestamp")
)

# 写入分布式存储(按日期分区)
cleaned.write.partitionBy("date").parquet("hdfs://output/cleaned_logs")

五、调试与监控

1. Web UI

  • 地址http://<driver-node>:4040
  • 功能:查看任务DAG、Stage详情、Executor资源占用

2. 日志定位

# 查看Executor日志
yarn logs -applicationId <app_id> -log_files stdout

3. 动态调整

# 运行时获取配置
spark.conf.get("spark.executor.memory")

# 动态修改(部分参数生效)
spark.conf.set("spark.sql.shuffle.partitions", "500")

六、常见问题解决

  1. OOM 错误

    • 增加 executor-memory
    • 减少 executor-cores(降低并行度)
  2. 数据倾斜

    • 使用 salting 技术
    • 调整 spark.sql.adaptive.enabled=true(Spark 3.0+自动优化)
  3. 慢任务

    • 检查 spark.sql.shuffle.partitions 是否过小
    • 使用 DataFrame 替代 RDD(Catalyst优化器更高效)

七、与非分布式代码的对比

操作Pandas(单机)PySpark(分布式)
读取 100GB CSV内存不足崩溃自动分片到多个节点
groupby().mean()单线程计算并行聚合后合并结果
自定义函数直接调用需序列化函数到各节点(udfpandas_udf

总结

PySpark 分布式使用的关键步骤:

  1. 配置集群连接(YARN/Standalone/K8s)
  2. 合理分区数据(避免倾斜)
  3. 优化资源参数(内存/CPU/并行度)
  4. 选择高效算子(避免 collect() 等动作)

实际生产中建议:

  • 小数据量先用 Pandas 测试逻辑
  • 大数据量通过 spark-submit 提交到集群
  • 监控 Spark UI 持续调优