PySpark 的分布式计算能力依赖于 集群管理框架(如 Spark Standalone、YARN、Mesos 或 Kubernetes)和 合理的代码设计。以下是详细的使用指南:
一、集群部署模式
PySpark 支持多种集群管理模式,配置方式如下:
| 模式 | 适用场景 | 启动命令示例 |
|---|---|---|
| Local | 单机测试(非分布式) | spark = SparkSession.builder.master("local[4]").getOrCreate() |
| Standalone | 专用 Spark 集群 | --master spark://master:7077 |
| YARN | Hadoop 生态集成 | --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-executors | Executor数量 | 10 |
spark.sql.shuffle.partitions | Shuffle分区数 | 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")
六、常见问题解决
-
OOM 错误
- 增加
executor-memory - 减少
executor-cores(降低并行度)
- 增加
-
数据倾斜
- 使用
salting技术 - 调整
spark.sql.adaptive.enabled=true(Spark 3.0+自动优化)
- 使用
-
慢任务
- 检查
spark.sql.shuffle.partitions是否过小 - 使用
DataFrame替代RDD(Catalyst优化器更高效)
- 检查
七、与非分布式代码的对比
| 操作 | Pandas(单机) | PySpark(分布式) |
|---|---|---|
| 读取 100GB CSV | 内存不足崩溃 | 自动分片到多个节点 |
groupby().mean() | 单线程计算 | 并行聚合后合并结果 |
| 自定义函数 | 直接调用 | 需序列化函数到各节点(udf 或 pandas_udf) |
总结
PySpark 分布式使用的关键步骤:
- 配置集群连接(YARN/Standalone/K8s)
- 合理分区数据(避免倾斜)
- 优化资源参数(内存/CPU/并行度)
- 选择高效算子(避免
collect()等动作)
实际生产中建议:
- 小数据量先用
Pandas测试逻辑 - 大数据量通过
spark-submit提交到集群 - 监控
Spark UI持续调优