3. 离线特征回放架构

离线特征回放是金融风控与推荐场景中验证历史特征有效性的核心手段。其核心思路是:在 T+1 时刻,利用历史快照数据,批量计算并存储过去某一时间窗口内的特征值,供后续模型训练、回测或策略分析使用。本章将深入解析基于 Hive 与 Spark 的批量回放架构,涵盖流程、计算存储与调度设计。

3.1 批量回放整体流程

离线回放流程通常遵循“数据抽取 → 特征计算 → 结果存储 → 调度编排”的线性流水线。以下 SVG 图展示了基于 Hive/Spark 的典型批量回放架构。

离线特征回放架构:Hive/Spark 批量回放流程 原始数据源 (Hive) 分区裁剪 & 清洗 Spark 特征计算 T+1 特征存储 离线调度中心 回放结果宽表 架构说明: • 数据流:Hive 原始表 → 分区裁剪 → Spark 分布式计算 → T+1 分区写入。 • 调度中心统一编排任务依赖,确保每个 T+1 分区在数据就绪后触发计算。 • 回放结果宽表按日期分区存储,支持按时间范围快速回溯。
关键设计原则:离线回放必须保证“时间一致性”,即计算特征时只能使用该时间点之前的历史数据,严禁未来信息泄露。Hive 的分区裁剪与 Spark 的 DataFrame API 天然支持这种时间约束。

3.2 T+1 特征计算与存储

T+1 是离线特征回放中最常见的计算粒度。其含义是:在交易日 T 结束后,利用截至 T 日收盘的全部数据,计算 T 日的特征,并于 T+1 日将结果写入存储层。

3.2.1 特征计算逻辑

以下代码片段展示了使用 Spark SQL 进行 T+1 特征计算的典型实现。该示例计算了用户过去 7 天的交易频次与金额均值。

-- 示例:T+1 用户交易特征计算 (Spark SQL)
INSERT OVERWRITE TABLE dwd_feature_user_trade_di PARTITION (dt = '${yesterday}')
SELECT
    user_id,
    COUNT(DISTINCT trade_id) AS trade_cnt_7d,
    ROUND(AVG(trade_amt), 2) AS trade_avg_amt_7d,
    CURRENT_TIMESTAMP() AS feature_update_time
FROM
    dwd_trade_detail_di
WHERE
    dt BETWEEN '${date_7d_ago}' AND '${yesterday}'  -- 严格使用历史窗口
    AND user_id IS NOT NULL
GROUP BY
    user_id;
时间窗口约束:上述 SQL 中的 dt 分区条件确保了特征计算仅依赖历史数据。实际生产环境中,${yesterday} 和 ${date_7d_ago} 由调度系统动态传入,避免硬编码。

3.2.2 存储层设计

离线特征存储通常采用 Hive 分区表或 ORC/Parquet 列式格式,以平衡查询性能与存储成本。下表对比了两种常见存储方案。

存储方案 格式 压缩比 查询性能 适用场景
Hive 分区表 (ORC) ORC + Zlib 高 (3-5x) 高 (谓词下推) 大规模离线回放、全量扫描
Hive 分区表 (Parquet) Parquet + Snappy 中 (2-3x) 高 (列裁剪) Spark 原生集成、点查场景
HBase (二级索引) KV 存储 低 极高 (RowKey 查询) 实时/近线特征回放

在金融场景中,推荐使用 Hive ORC 分区表 作为离线回放的主存储。其高压缩比可显著降低 HDFS 存储成本,而 ORC 的谓词下推能力能加速按日期分区的特征回溯。

3.3 离线回放调度设计

调度系统是离线回放架构的“中枢神经”。一个健壮的调度设计需要解决任务依赖、重试机制、数据回溯与监控告警四大问题。

3.3.1 调度层级与依赖

典型的离线回放调度分为三个层级:

  1. 数据就绪层:检查上游 Hive 表对应分区是否已写入完成(例如通过 hdfs dfs -test -e 或 Hive Metastore 元数据校验)。
  2. 特征计算层:执行 Spark 任务,读取数据就绪层的分区,计算特征并写入目标分区。
  3. 结果校验层:对产出特征进行空值率、分布漂移等质量校验,通过后标记分区为“可用”状态。

以下 SVG 图展示了调度任务之间的依赖关系与执行顺序。

离线回放调度依赖关系 D1: 数据就绪检查 D2: 特征计算任务 D3: 结果质量校验 重试 & 告警机制 失败/超时 调度策略:D1 成功 → D2 执行;D2 失败则自动重试 3 次,间隔 5 分钟;D3 校验不通过则触发告警并阻断下游消费。

3.3.2 调度配置示例

以下是一个基于 Apache DolphinScheduler 的离线回放任务定义片段,展示了参数传递与依赖配置。

{
  "name": "offline_feature_replay_daily",
  "schedule": "0 2 * * *",          // 每天凌晨 2 点执行
  "task_list": [
    {
      "name": "check_data_ready",
      "type": "SHELL",
      "command": "hdfs dfs -test -e /user/hive/warehouse/dwd_trade_detail_di/dt=${yesterday}"
    },
    {
      "name": "compute_user_features",
      "type": "SPARK",
      "program": "spark-submit --class com.risk.feature.ReplayJob feature-replay.jar",
      "params": {
        "input_dt": "${yesterday}",
        "output_dt": "${yesterday}"
      },
      "deps": ["check_data_ready"]
    },
    {
      "name": "validate_feature_quality",
      "type": "SHELL",
      "command": "python3 validate_feature.py --dt ${yesterday}",
      "deps": ["compute_user_features"]
    }
  ]
}
调度避坑指南:
  • 避免使用“固定日期”作为分区参数,必须使用调度系统内置的日期变量(如 ${yesterday})。
  • 特征计算任务应配置合理的资源上限(executor 数量与内存),防止因数据倾斜导致 OOM。
  • 对于跨多天的回溯需求(如回放过去 30 天特征),建议采用“循环调度”或“多实例并行”模式,而非单任务串行处理。

3.4 本章小结

离线特征回放架构的核心在于“时间一致性”与“批量高效”。通过 Hive 的分区裁剪确保数据时间边界,Spark 的分布式计算提供吞吐能力,T+1 的分区存储简化数据管理,而完善的调度设计则保障了整个流水线的稳定运行。在实际金融场景中,该架构可支撑每日数百亿条交易记录的离线特征回溯,是模型迭代与策略验证的基础设施。