3. Spark批处理基础:核心概念与作业提交流程

在风控特征工程中,每日需处理数亿至数十亿条用户行为日志、交易流水与外部数据。Spark作为分布式批处理引擎,是特征分层加工的核心计算平台。本章聚焦Spark RDD与DataFrame两大抽象、SparkSession的初始化规范以及批处理作业的完整提交流程,为后续特征计算Pipeline打下基础。

3.1 Spark核心抽象:RDD与DataFrame

特性 RDD (Resilient Distributed Dataset) DataFrame
本质 不可变、分区的分布式对象集合 带Schema的分布式行集合,类似关系表
数据表示 Java/Python对象,无内置Schema Row对象 + StructType Schema
优化能力 依赖开发者手动优化(如使用mapPartitions) Catalyst优化器自动执行谓词下推、列剪枝、常量折叠
序列化 Java/Kryo序列化,开销较大 Tungsten二进制格式,堆外内存管理,性能高
API风格 函数式(map, filter, reduceByKey) 声明式(select, where, groupBy) + SQL
适用场景 非结构化数据、自定义复杂逻辑、需要细粒度控制 结构化/半结构化数据、SQL分析、特征加工
风控场景推荐 原始日志解析、自定义UDF无法覆盖的复杂转换 特征聚合、多表Join、窗口计算、特征宽表生成
选型建议: 风控特征工程中,90%以上的批处理任务应优先使用DataFrame API。仅在需要手动控制分区内迭代逻辑(如复杂状态计算)或使用第三方非结构化库时,才回退到RDD。

3.2 SparkSession初始化规范

SparkSession是Spark 2.0+的统一入口,替代了旧的SparkContext、SQLContext、HiveContext。在风控生产环境中,初始化需考虑资源配置、Hive集成与动态分配。

from pyspark.sql import SparkSession

spark = SparkSession.builder \
    .appName("risk_feature_batch") \
    .config("spark.sql.adaptive.enabled", "true") \
    .config("spark.sql.adaptive.coalescePartitions.enabled", "true") \
    .config("spark.sql.adaptive.coalescePartitions.minPartitionNum", "200") \
    .config("spark.sql.shuffle.partitions", "400") \
    .config("spark.dynamicAllocation.enabled", "true") \
    .config("spark.dynamicAllocation.minExecutors", "10") \
    .config("spark.dynamicAllocation.maxExecutors", "200") \
    .config("spark.sql.warehouse.dir", "/user/hive/warehouse") \
    .enableHiveSupport() \
    .getOrCreate()
配置项 推荐值 说明
spark.sql.adaptive.enabled true 开启自适应查询执行,自动合并小分区、调整Join策略
spark.sql.shuffle.partitions 200~400 Shuffle默认分区数,根据数据量调整,避免小文件过多
spark.dynamicAllocation.enabled true 动态资源分配,避免空闲Executor浪费集群资源
spark.sql.warehouse.dir Hive仓库路径 指定Hive表存储位置,需与Hive Metastore一致
生产注意: 不要在循环或函数内部重复创建SparkSession。一个Spark Application内应全局共享一个SparkSession实例,否则会导致JVM资源泄漏和Driver OOM。

3.3 批处理作业提交流程

一个完整的Spark批处理作业从提交到完成,经历以下阶段:

  1. 客户端提交:使用spark-submit脚本,指定主类、部署模式、资源配置与依赖JAR/Py文件。
  2. Driver启动:在集群的Gateway节点(client模式)或YARN ApplicationMaster(cluster模式)上启动Driver进程。
  3. SparkContext初始化:Driver创建SparkContext,向Cluster Manager(YARN/K8s)申请资源。
  4. Executor分配:Cluster Manager在Worker节点上启动Executor进程,并向Driver注册。
  5. 作业调度:Driver将用户代码转换为DAG(有向无环图),由DAGScheduler拆分为Stage,TaskScheduler将Task分发到Executor。
  6. 任务执行:Executor并行执行Task,数据在Shuffle阶段跨节点传输。
  7. 结果收集与清理:Driver收集最终结果(如保存到Hive表),释放所有Executor资源。
风控批处理提交流程示例:
spark-submit \
  --master yarn \
  --deploy-mode cluster \
  --queue risk_priority \
  --num-executors 100 \
  --executor-cores 4 \
  --executor-memory 8g \
  --driver-memory 4g \
  --conf spark.sql.adaptive.enabled=true \
  --py-files feature_lib.zip,config.py \
  risk_feature_pipeline.py \
  --date 2025-03-01 \
  --feature_type rolling_30d

3.4 核心流程SVG图

Spark批处理作业提交流程 Spark批处理作业提交流程 spark-submit 提交 Driver 启动 (AM) Cluster Manager 分配资源 Executor 启动 & 注册 DAG 拆分 & Task 分发 Task 并行执行 & Shuffle 结果写入Hive表 / HDFS,Driver收集Metrics,释放资源

3.5 RDD与DataFrame血缘关系SVG图

RDD与DataFrame血缘关系 RDD与DataFrame血缘关系与转换 RDD[Row] 无Schema toDF() / createDataFrame() DataFrame + Schema Catalyst 优化器 Tungsten 二进制执行 DataFrame通过Catalyst生成优化物理计划,Tungsten以二进制格式管理内存,性能比RDD提升3~10倍

3.6 风控批处理最佳实践

实践项 具体做法 收益
分区策略 按日期分区(dt字段),每个分区数据量控制在128MB~1GB 避免小文件过多,提升读取并行度
列剪枝 只select特征计算所需的列,避免select * 减少I/O与网络传输量30%~70%
谓词下推 在读取时就过滤无效数据(如where dt='2025-03-01') 减少Shuffle数据量,加速计算
广播变量 将维度表(如黑名单、设备指纹库)广播到每个Executor 避免大表Join的Shuffle开销
缓存策略 对多次使用的中间结果使用.cache()或.persist(StorageLevel.MEMORY_AND_DISK) 避免重复计算,缩短DAG执行时间
Shuffle调优 设置spark.sql.adaptive.coalescePartitions.enabled=true自动合并小分区 减少Shuffle文件数,降低Driver压力
风控特征批处理典型Pipeline:
# 1. 读取原始日志(按分区过滤)
raw_df = spark.table("ods.user_behavior_log") \
    .where(col("dt") == batch_date)

# 2. 特征加工(窗口聚合)
feature_df = raw_df.groupBy("user_id") \
    .agg(
        count("*").alias("total_actions_30d"),
        sum(when(col("action_type") == "login", 1).otherwise(0)).alias("login_cnt_30d"),
        avg(col("amount")).alias("avg_amount_30d")
    )

# 3. 关联维度表(广播)
dim_df = spark.table("dim.user_risk_level").hint("broadcast")
result_df = feature_df.join(dim_df, "user_id", "left")

# 4. 写入特征宽表
result_df.write \
    .mode("overwrite") \
    .partitionBy("dt") \
    .format("parquet") \
    .saveAsTable("feature.user_rolling_features")
生产监控指标: 每次批处理作业完成后,记录spark.sql.shuffle.partitions实际Shuffle数据量、Stage耗时、GC时间与Executor峰值内存。这些指标是后续调优与资源预算的核心依据。