4. 特征计算框架选型:Flink、Spark Streaming、Dask,轻量级方案对比

做实时特征工程,选框架这件事,我踩过的坑比写过的代码还多。

你想想看,行情数据每秒几百笔,订单簿每毫秒都在变。选错了框架,轻则延迟爆炸,重则直接OOM崩掉。今天我就把几个主流方案掰开揉碎讲清楚。

4.1 三大框架的核心定位

先给个全景图,方便你建立直觉。

一句话总结:

  • Flink:真正的流处理之王,事件时间、精确一次语义,天生为实时而生
  • Spark Streaming:微批次架构,生态丰富,适合与批处理混用
  • Dask:轻量级并行计算库,上手快,适合中小规模策略
实时特征计算框架选型全景图 Apache Flink 真正的流处理 ✅ 事件时间语义 ✅ 精确一次语义 ✅ 毫秒级延迟 ✅ 状态管理强大 ✅ 窗口操作丰富 ⚠ 运维成本高 ⚠ 学习曲线陡 适合:高频/中频策略 Spark Streaming 微批次架构 ✅ 生态最丰富 ✅ 批流一体 ✅ 社区活跃 ✅ 与MLlib集成 ✅ 部署简单 ⚠ 秒级延迟 ⚠ 状态管理弱 适合:中频/低频策略 Dask 轻量级并行 ✅ 上手极快 ✅ 与Pandas兼容 ✅ 单机即可运行 ✅ 调试方便 ✅ 资源开销小 ⚠ 无状态管理 ⚠ 容错弱 适合:研究/小规模

4.2 Flink:为实时而生的硬核选手

我个人习惯把Flink叫做「真·流处理」。为什么?因为它处理的是事件流,而不是微批次。

举个例子,你要计算过去5分钟的VWAP(成交量加权均价)。在Flink里,你只需要定义一个滑动窗口,数据来了就更新,延迟在毫秒级。

// Flink SQL 实现5分钟VWAP
CREATE VIEW vwap_5min AS
SELECT 
  symbol,
  SUM(price * volume) / SUM(volume) AS vwap,
  TUMBLE_END(event_time, INTERVAL '5' MINUTE) AS window_end
FROM trades
GROUP BY 
  symbol, 
  TUMBLE(event_time, INTERVAL '5' MINUTE);

我的经验: 做高频策略时,Flink的状态后端我推荐用RocksDB。默认的HashMap状态后端在数据量大时容易OOM。我曾经有个策略,状态量到了50GB,换成RocksDB后稳如老狗。

Flink的事件时间语义是它的杀手锏。行情数据经常乱序到达,比如A股Level-2数据,不同交易所的推送延迟不一样。Flink的watermark机制能优雅处理这个问题。

避坑指南: 我曾经在Flink里设置watermark延迟为5秒,结果某天行情剧烈波动,数据延迟到了8秒。那天的特征全部算错,回测收益直接腰斩。后来我改成动态watermark,根据实时延迟自适应调整,才解决这个问题。

4.3 Spark Streaming:生态为王

Spark Streaming用的是微批次架构,说白了就是把流切成一个个小批次,比如每2秒一批。这样做的好处是能和Spark批处理无缝衔接。

我见过很多团队,白天用Spark做盘后分析,晚上用Spark Streaming做实时特征。一套代码,两套用法,运维成本确实低。

# PySpark 实现滚动窗口特征
from pyspark.sql import functions as F
from pyspark.sql.window import Window

# 定义5分钟滚动窗口
window_spec = (
    Window
    .partitionBy("symbol")
    .orderBy(F.col("event_time").cast("long"))
    .rangeBetween(-300, 0)  # 过去300秒
)

# 计算滚动特征
features_df = trades_df.withColumn(
    "vwap_5min", 
    F.sum(F.col("price") * F.col("volume")).over(window_spec) / 
    F.sum("volume").over(window_spec)
)

关键对比:

  • Flink的窗口是数据驱动的,数据到了就触发计算
  • Spark Streaming的窗口是时间驱动的,每批次触发一次
  • 这意味着Spark Streaming的延迟至少是一个批次间隔(通常1-5秒)

你想想看,如果你的策略需要秒级响应,Spark Streaming可能不太够用。但如果是分钟级的中频策略,它完全够用,而且生态优势很明显。

4.4 Dask:轻量级选手的逆袭

说到Dask,我其实挺喜欢它的。它不像Flink那么重,也不像Spark Streaming那么「批」。它就是一个轻量级的并行计算库,能让你在单机上模拟分布式计算。

我刚开始做量化时,就用Dask做实时特征。那时候策略规模小,一天几百万笔数据,Dask完全扛得住。

import dask.dataframe as dd
from dask.distributed import Client

# 启动本地集群
client = Client(n_workers=4, threads_per_worker=2)

# 读取实时数据流(模拟)
df = dd.read_csv('trades_*.csv', blocksize='100MB')

# 计算滚动特征
features = df.groupby('symbol').apply(
    lambda x: x.sort_values('time').rolling('300s').agg({
        'price': 'mean',
        'volume': 'sum'
    }),
    meta={'price_mean': 'f8', 'volume_sum': 'f8'}
)

我的建议: Dask最适合的场景是策略研究和回测阶段。你可以在本地快速验证特征逻辑,等确认没问题了,再迁移到Flink或Spark上跑生产。这样开发效率能提升好几倍。

但Dask有个硬伤——没有内置的状态管理和容错机制。一旦任务挂了,你得自己处理恢复逻辑。生产环境我一般不推荐用Dask做核心链路。

4.5 选型决策矩阵

说了这么多,到底怎么选?我整理了一个表格,方便你对照决策。

评估维度 Flink Spark Streaming Dask
延迟 毫秒级 秒级 秒级(受限于调度)
吞吐量 极高(百万级/秒) 高(十万级/秒) 中等(万级/秒)
状态管理 ★★★★★ ★★★☆☆ ★☆☆☆☆
运维复杂度
学习成本
Python生态 中(PyFlink) 高(PySpark) 极高(原生Python)
适合策略频率 高频/中频 中频/低频 研究/小规模

4.6 我的实战建议

嗯,这里要注意,选型不是非黑即白的。我见过不少团队混合使用:

  • 研究阶段:用Dask快速验证特征逻辑
  • 生产阶段:用Flink跑核心高频特征
  • 辅助链路:用Spark Streaming做离线特征和批处理

说白了,工具是死的,人是活的。你只要搞清楚每个框架的核心优势适用边界,就能做出合理的选择。

最后说一句: 别为了用Flink而用Flink。如果你的策略只需要分钟级特征,Spark Streaming完全够用,何必给自己增加运维负担?我见过太多团队,技术选型时追求「高大上」,结果运维成本把策略收益全吃掉了。

蓝海数据掘金营,专注资料整理