3. 离线特征系统架构:批处理架构

离线特征系统是特征工程体系中最成熟、最稳定的部分。其核心采用 Lambda 架构的批处理层,以“全量计算、周期调度、不可变存储”为设计原则,为模型训练和批量推理提供高一致性的特征数据。本章深入剖析批处理层的架构设计、存储选型与计算流程。

3.1 Lambda 架构中的批处理层

Lambda 架构将数据流分为批处理层(Batch Layer)、速度层(Speed Layer)和服务层(Serving Layer)。离线特征系统对应 批处理层,承担以下职责:

  • 全量数据重算:每次调度周期(如每日凌晨)读取全量历史数据,重新计算所有特征,保证结果可复现。
  • 数据不可变:原始数据以 append-only 方式存储,批处理层输出结果写入不可变存储,支持时间旅行查询。
  • 高一致性:通过事务性写入和幂等计算,确保同一份数据在不同时间重算结果完全一致。
  • 容错与重试:任务失败后可从检查点(Checkpoint)恢复,或基于原始数据全量重跑。
关键设计原则:批处理层是“真相之源”(Source of Truth)。所有实时特征在回放验证时,必须以批处理层产出的离线特征为基准进行比对。

3.2 离线特征存储:Hive / HDFS

离线特征存储选型以 Hive 表 + HDFS 文件 为事实标准。其核心优势在于:

维度 Hive / HDFS 备选方案(如 HBase)
存储模型 列式 ORC / Parquet,高压缩比 宽表存储,行式访问
查询模式 批量扫描、分区裁剪 点查、小范围扫描
一致性 ACID(Hive 4+),快照隔离 最终一致性
成本 低成本,基于 HDFS 副本 高内存成本
适用场景 全量特征回放、训练集构建 在线特征服务

离线特征表通常按 日期分区(如 dt=2025-03-01),每个分区内存储该日所有实体的特征快照。典型表结构如下:

CREATE TABLE offline_user_features (
  user_id      STRING,
  feature_date STRING,
  age          INT,
  city         STRING,
  last_7d_click_cnt INT,
  last_7d_order_amt  DOUBLE,
  -- ... 其他特征列
  update_time  TIMESTAMP
)
PARTITIONED BY (dt STRING)
STORED AS PARQUET
TBLPROPERTIES ('parquet.compression'='SNAPPY');
分区策略建议:使用 dt 作为分区键,避免使用 feature_date 作为分区字段。前者是 Hive 保留字,性能更优;后者可能导致小文件过多。

3.3 离线特征计算流程

离线特征计算遵循 ETL 管道 模式,分为三个核心阶段:数据接入、特征计算、结果写入。以下 SVG 图展示了完整的批处理架构与数据流。

离线特征批处理架构图 离线特征批处理架构(Lambda 批处理层) 数据源层 业务日志 / 数据库 Binlog 原始数据存储 HDFS 原始层(append-only) 调度与编排 Airflow / DolphinScheduler 特征计算层(批处理 ETL) 数据清洗 & 去重 Spark / Hive SQL 窗口聚合计算 滑动窗口 / 累计窗口 特征拼接 & 编码 One-Hot / Embedding 质量校验 空值 / 分布漂移 离线特征存储 Hive ORC / Parquet 按 dt 分区,不可变快照 特征元数据 特征名称 / 类型 / 版本 Hive Metastore 特征回放基线 全量历史特征快照 用于实时特征对比 模型训练 全量特征宽表 批量推理 T+1 离线打分 特征回放验证 与实时特征对比

3.4 批处理计算流程详解

一个典型的离线特征批处理任务包含以下步骤,以每日凌晨 T+1 调度为例:

  1. 数据读取:从 HDFS 原始层读取前一日(dt = yesterday)的全量业务日志,包括用户行为、订单、点击等。
  2. 数据清洗:过滤异常数据(如空 user_id、时间戳越界),去重(基于事件 ID 去重),格式统一。
  3. 窗口聚合:按用户 ID 分组,计算滑动窗口特征(如近 7 天点击次数、近 30 天消费金额)。窗口边界由业务定义,通常使用 Spark SQL 的 RANGE BETWEEN 或自定义 UDAF。
  4. 特征拼接:将多个窗口特征、基础属性特征、交叉特征拼接为宽表,每行对应一个用户在某日的特征快照。
  5. 质量校验:检查空值率、特征分布是否漂移、主键是否唯一。若校验失败,触发告警并阻断下游写入。
  6. 分区写入:将结果写入 Hive 表对应分区(dt = yesterday),使用 INSERT OVERWRITE 保证幂等性。
常见陷阱:窗口聚合时若使用 UNBOUNDED PRECEDING 可能导致数据倾斜。建议对高频用户做分桶处理,或使用 RANGE BETWEEN INTERVAL '7' DAY PRECEDING AND CURRENT ROW 限定窗口范围。

3.5 离线特征与实时特征回放对比的架构映射

在特征回放场景中,离线特征系统作为 基准系统,为实时特征提供“标准答案”。以下表格对比两者在批处理层中的角色差异:

对比维度 离线特征(批处理层) 实时特征(速度层)
计算时间 T+1 批量计算 事件发生后毫秒级
数据范围 全量历史数据 近期窗口数据(如 7 天)
一致性 强一致性,可重算 最终一致性,可能丢数据
存储 Hive ORC / Parquet Redis / HBase / Kafka
回放角色 基线(Ground Truth) 待验证对象

回放验证时,离线特征系统提供 全量历史快照,实时特征系统按相同时间窗口回放事件流,两者在特征值层面逐字段比对。任何偏差都会触发告警,驱动实时计算逻辑修正。

最佳实践:在离线特征表中增加 feature_version 列,记录特征计算逻辑的版本号。回放时,实时特征必须使用相同版本的计算逻辑,避免因逻辑变更导致误报。