2. Spark 基础入门:发展史、Hadoop 对比、核心组件与运行架构
在催收触达场景中,每天需要处理数千万条通话记录、短信回执、IVR 按键流和还款行为日志。传统单机或简单批处理框架难以在 30 分钟内完成全量数据的清洗、聚合与模型评分。Spark 凭借其内存计算和统一引擎,成为催收数据中台的首选计算框架。本节从发展史、与 Hadoop 的对比、核心组件和运行架构四个维度,帮你建立 Spark 的全局认知。
2.1 Spark 发展史:从实验室到大数据标配
Spark 诞生于加州大学伯克利分校 AMPLab,最初是为了解决 MapReduce 在迭代计算和交互式查询上的性能瓶颈。以下是关键里程碑:
| 时间 | 版本 / 事件 | 核心意义 |
|---|---|---|
| 2009 | Spark 0.5 原型 | AMPLab 启动项目,基于内存的分布式计算框架 |
| 2010 | 开源发布 | 采用 Berkeley Software Distribution 许可,社区开始关注 |
| 2013 | Apache 孵化器 | 加入 Apache 软件基金会,进入标准化治理 |
| 2014 | Spark 1.0 | 引入 Spark SQL、MLlib、Streaming 和 GraphX,形成统一平台 |
| 2016 | Spark 2.0 | Dataset/DataFrame 成为一等公民,Tungsten 和 Catalyst 优化引擎 |
| 2020+ | Spark 3.x | 自适应查询执行、动态分区裁剪、GPU 加速支持 |
催收场景启示: Spark 2.0 之后的 DataFrame API 和 Catalyst 优化器,让催收分析师可以用 SQL 直接处理千亿级通话记录,无需编写复杂的 MapReduce 代码。
2.2 Spark 与 Hadoop 对比:不只是快
很多人认为 Spark 是 Hadoop 的替代品,实际上两者在架构哲学上有本质区别。催收业务中,Hadoop 适合海量历史归档的批量存储,Spark 则擅长需要快速迭代的实时特征计算。
| 对比维度 | Hadoop MapReduce | Apache Spark |
|---|---|---|
| 计算模式 | 磁盘迭代,每个 stage 写 HDFS | 内存 DAG,尽量在内存中完成计算 |
| 中间结果 | 必须落盘,IO 开销大 | 优先内存缓存,支持 checkpoint |
| 延迟 | 分钟级(任务启动 + 磁盘读写) | 秒级到分钟级(内存计算 + 快速调度) |
| 编程接口 | 仅 Map 和 Reduce 两个阶段 | RDD、DataFrame、Dataset、SQL、MLlib |
| 迭代计算 | 每次迭代重新读取数据,效率低 | 内存中反复使用,适合机器学习训练 |
| 流处理 | 不支持原生流 | Structured Streaming,微批 + 连续处理 |
| 容错机制 | 任务级别重试 + 数据副本 | RDD Lineage 血统 + Checkpoint |
| 典型催收场景 | 历史通话详单归档、合规审计日志存储 | 实时逾期评分、催收策略动态计算、通话特征提取 |
选型建议: 催收数据平台通常采用 Hadoop HDFS 做底层存储,Spark 做计算引擎。HDFS 存放原始通话录音索引和还款流水,Spark 负责每日触达策略的批量评分和实时催收队列刷新。
2.3 Spark 核心组件:统一生态的四大支柱
Spark 不是一个单一的计算引擎,而是一个包含 SQL、流、机器学习和图计算的统一平台。催收业务中,这四个组件覆盖了从数据清洗到模型部署的全链路。
常见误区: 催收团队常把 Spark 当作“快一点的 Hive”来用,只使用 Spark SQL。实际上,在实时触达评分和共债图分析场景中,Streaming 和 GraphX 能带来 5-10 倍的效率提升。建议在架构设计阶段就规划好组件分工。
2.4 Spark 运行架构:Driver、Executor 与集群管理器
理解 Spark 的运行架构,是调优催收任务性能的前提。一个 Spark 应用在集群中如何分配资源、如何并行执行,直接决定了每日触达任务能否在业务 SLA 内完成。
2.4.1 核心角色
| 角色 | 职责 | 催收场景类比 |
|---|---|---|
| Driver | 运行 main() 方法,创建 SparkContext,解析 DAG,调度 Task 到 Executor | 催收策略调度中心:拆解任务、分配工单 |
| Executor | 在 Worker 节点上运行 Task,缓存数据,返回结果给 Driver | 催收坐席:执行具体的触达动作(外呼、短信) |
| Cluster Manager | 管理集群资源,分配 Executor 所需的 CPU 和内存 | 催收人力排班系统:决定每个时段安排多少坐席 |
| Task | Executor 上最小的执行单元,对应一个分区上的计算逻辑 | 单个催收工单:对一个客户执行一次触达 |
2.4.2 任务提交流程
- 提交应用: 使用
spark-submit将打包好的催收任务 JAR 或 Python 脚本提交到集群。 - 启动 Driver: Cluster Manager 分配一台机器运行 Driver 进程,Driver 创建 SparkContext。
- 申请资源: SparkContext 向 Cluster Manager 申请 Executor 资源(CPU 核数、内存大小)。
- 启动 Executor: Cluster Manager 在 Worker 节点上启动 Executor 进程,并向 Driver 注册。
- 分发任务: Driver 将用户代码(如 DataFrame 转换)转换成 DAG,再拆分为 Stage 和 Task,分发到 Executor 执行。
- 结果汇聚: Executor 执行 Task 并将结果写回 HDFS 或直接返回 Driver,Driver 更新催收结果表。
2.4.3 催收任务调优要点
- 并行度设置:
spark.sql.shuffle.partitions建议设置为 Executor 总核数的 2-3 倍。催收宽表 JOIN 操作多,分区数不足会导致内存溢出。 - 内存分配: Executor 内存中,
spark.memory.fraction默认 0.6,剩余留给用户代码。催收特征计算常涉及大字典广播,需适当调大spark.memory.offHeap.size。 - 数据本地性: 尽量让 Task 在数据所在的节点上执行,减少网络传输。催收数据通常存储在 HDFS 上,合理设置
spark.locality.wait避免任务等待。 - 广播变量: 催收策略配置表(如催收等级映射、节假日规则)通常较小,使用
broadcast代替普通 JOIN,可减少 Shuffle 数据量 50% 以上。
实战案例: 某催收平台每日处理 2000 万条通话记录,初始使用默认配置,任务运行 45 分钟。通过将 Executor 数量从 10 调整为 20,并设置
spark.sql.shuffle.partitions=60,任务缩短至 18 分钟,满足 20 分钟 SLA 要求。