第2章 Spark核心概念回顾:RDD、DataFrame、Dataset、SparkSession、DAG调度器、内存计算原理
在深入回测加速实战之前,必须牢固掌握Spark的五大核心抽象与两大运行机制。本章将系统梳理RDD、DataFrame、Dataset、SparkSession、DAG调度器以及内存计算原理,为后续的并行回测优化打下坚实的理论基础。
2.1 核心数据抽象对比
Spark提供了三种主要的数据抽象:RDD、DataFrame和Dataset。理解它们的差异与适用场景,是选择正确回测数据结构的前提。
| 特性 | RDD (Resilient Distributed Dataset) | DataFrame | Dataset |
|---|---|---|---|
| 引入版本 | Spark 1.0 | Spark 1.3 | Spark 1.6 |
| 数据表示 | Java/Scala对象,无Schema | Row对象,带Schema(列名+类型) | 强类型JVM对象,带Schema |
| 类型安全 | 编译时类型安全 | 运行时类型检查 | 编译时类型安全 |
| 性能优化 | 依赖开发者手动优化 | Catalyst优化器自动优化 | Catalyst + Tungsten编码优化 |
| 序列化方式 | Java/Kryo序列化 | 列式二进制格式(Tungsten) | Encoders编码(高效二进制) |
| 适用场景 | 底层转换、非结构化数据 | SQL分析、ETL、回测指标聚合 | 复杂业务逻辑、类型安全要求高 |
回测选型建议: 在回测场景中,DataFrame 是最常用的选择。它既能享受Catalyst优化器的自动谓词下推和列剪枝,又能通过SQL表达复杂的信号计算逻辑。当需要处理自定义策略对象且要求类型安全时,优先使用 Dataset。RDD通常只用于底层数据清洗或自定义分区器。
2.2 SparkSession:统一入口
SparkSession是Spark 2.0之后唯一的编程入口,整合了SQLContext、HiveContext和StreamingContext的功能。在回测应用中,通过SparkSession可以一站式完成数据读取、SQL查询、UDF注册和配置管理。
// 创建SparkSession,配置回测所需资源
val spark = SparkSession.builder()
.appName("BacktestEngine")
.config("spark.sql.shuffle.partitions", "200") // 控制shuffle并行度
.config("spark.memory.fraction", "0.6") // 统一内存管理比例
.config("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
.getOrCreate()
// 注册回测所需的临时视图
val tradeDF = spark.read.parquet("hdfs://data/trades")
tradeDF.createOrReplaceTempView("trades")
// 执行回测SQL
val signalDF = spark.sql("""
SELECT
stock_code,
trade_date,
close_price,
MA(close_price, 5) OVER (PARTITION BY stock_code ORDER BY trade_date) AS ma5
FROM trades
""")
2.3 DAG调度器:回测任务的执行蓝图
DAG(有向无环图)调度器是Spark的调度核心。它将用户编写的转换操作(Transformation)构建成一个无环的Stage执行图,并决定哪些任务可以并行执行、哪些需要Shuffle。
关键机制: DAG调度器将作业划分为多个Stage,每个Stage包含一组可以流水线执行的窄依赖任务。宽依赖(Shuffle)会触发Stage边界,这也是回测中性能瓶颈的常见来源。
2.4 内存计算原理:回测加速的基石
Spark的内存计算能力是回测加速的核心。它通过统一内存管理(Unified Memory Management)将Executor内存划分为执行内存(Execution)和存储内存(Storage),两者可以动态抢占。
- 执行内存: 用于Shuffle、Join、Sort、Aggregation等操作中的中间数据缓冲。回测中的窗口函数和分组聚合大量依赖此区域。
- 存储内存: 用于缓存RDD/DataFrame/Dataset以及广播变量。回测中可以将历史行情表缓存到内存,避免反复读取HDFS。
- 动态占用机制: 当执行内存不足时,可以抢占存储内存的空闲空间,反之亦然。但抢占可能导致缓存块被驱逐(Eviction),需合理设置
spark.memory.storageFraction。
回测内存调优警告: 如果回测涉及大量历史数据缓存(如全量日线行情),建议将
spark.memory.storageFraction 从默认0.5调高至0.6~0.7,避免执行阶段频繁驱逐缓存数据导致重复计算。
2.5 核心概念在回测中的映射
将上述概念映射到回测场景,可以更直观地理解每个组件的作用:
| Spark概念 | 回测中的对应角色 | 性能影响 |
|---|---|---|
| RDD | 原始行情数据的底层表示,用于自定义分区或容错恢复 | 序列化开销大,适合低频率操作 |
| DataFrame | 存储股票日线行情、财务指标、因子值,支持SQL信号计算 | Catalyst优化可减少50%以上扫描量 |
| Dataset | 封装策略信号对象、交易指令,保证类型安全 | Tungsten编码减少GC压力 |
| SparkSession | 回测引擎入口,管理Hive元数据、UDF、配置参数 | 统一配置,避免资源碎片 |
| DAG调度器 | 将回测流程(读取→清洗→信号→聚合)划分为Stage | 减少Shuffle次数直接提升回测速度 |
| 内存计算 | 缓存历史行情、广播股票池、中间结果复用 | 避免磁盘I/O,加速迭代回测 |