4. 离线特征工程基础:数据源接入、特征计算与存储

离线特征工程是批流一体特征平台的基石。本章聚焦离线链路的核心三环节:数据源接入(Hive/HDFS)、特征计算(Spark SQL)和特征存储(HBase/MySQL)。掌握这三步,即可构建稳定、可回溯的离线特征生产管线。

4.1 数据源接入:Hive 与 HDFS

离线特征的数据源通常来自数据仓库的 Hive 表或 HDFS 上的原始日志文件。接入阶段需要解决分区发现、数据格式和读取效率三个问题。

数据源类型 典型场景 读取方式 注意事项
Hive 表 用户画像、订单宽表 Spark SQL 直接读取 关注分区裁剪、小文件合并
HDFS 文件 埋点日志、实时落盘 Spark DataFrame 读取 指定 schema、处理脏数据
Kafka(离线回溯) 历史消息回放 Spark Streaming 批模式 设置合理的 offset 策略
最佳实践:离线特征接入时,优先使用 Hive 表的增量分区(如 dt=2025-04-01),避免全表扫描。对于 HDFS 原始日志,建议先做一次 ETL 清洗,再写入 Hive 中间表,形成统一的特征数据源。

4.2 特征计算:Spark SQL 核心操作

Spark SQL 是离线特征计算的主力引擎。通过 SQL 或 DataFrame API,可以完成聚合统计、窗口计算、多表 Join 等特征加工逻辑。

4.2.1 典型特征计算模式

  • 统计特征:COUNT、SUM、AVG、MAX、MIN 等聚合函数,按用户/物品分组。
  • 时序特征:使用窗口函数(如 ROW_NUMBER、LAG、LEAD)计算近 N 天行为统计。
  • 交叉特征:多表 Join 后计算比率、差值或组合标签。
  • 分桶/离散化:使用 CASE WHEN 或 UDF 将连续值映射为类别。
-- 示例:计算用户近7天点击率特征
SELECT
  user_id,
  COUNT(*) AS total_click,
  SUM(CASE WHEN action = 'buy' THEN 1 ELSE 0 END) AS buy_cnt,
  ROUND(SUM(CASE WHEN action = 'buy' THEN 1 ELSE 0 END) / COUNT(*), 4) AS buy_rate_7d
FROM user_behavior
WHERE dt >= date_sub('2025-04-01', 7) AND dt <= '2025-04-01'
GROUP BY user_id;
性能要点:Spark SQL 特征计算时,优先使用列式存储(Parquet/ORC)和动态分区插入。对于大表 Join,开启 Broadcast Join 或 Sort Merge Join 优化,避免 Shuffle 数据倾斜。

4.3 特征存储:HBase 与 MySQL

离线特征计算完成后,需要将结果存储到在线可用的存储系统中。HBase 和 MySQL 是两种主流选择,分别适用于高并发随机读取和结构化查询场景。

对比维度 HBase MySQL
数据模型 宽表、稀疏列族 关系型、固定 schema
读写模式 随机读写、批量写入 随机读写、事务支持
扩展性 水平扩展、自动分片 主从复制、分库分表
典型特征场景 用户实时特征、物品画像 特征元数据、小规模配置
写入方式 BulkLoad / Put 批量 JDBC Batch Insert

4.3.1 HBase 存储设计要点

  • RowKey 设计:通常使用 user_id 或 item_id 作为前缀,避免热点。
  • 列族规划:按特征类型分列族(如 cf_stats、cf_seq),控制列族数量(建议 ≤3)。
  • TTL 设置:离线特征通常设置 7~30 天 TTL,自动过期旧数据。
-- HBase 建表示例(通过 HBase Shell)
create 'user_features', 
  {NAME => 'stats', VERSIONS => 1, TTL => 604800},
  {NAME => 'seq', VERSIONS => 1, TTL => 2592000}

4.3.2 MySQL 存储设计要点

  • 表结构:特征名作为列,每行对应一个实体(用户/物品)。
  • 索引:对 entity_id 建立唯一索引,支持快速点查。
  • 批量写入:使用 REPLACE INTO 或 INSERT … ON DUPLICATE KEY UPDATE 避免重复。
-- MySQL 特征表建表示例
CREATE TABLE user_features_daily (
  user_id VARCHAR(64) NOT NULL,
  feature_name VARCHAR(128) NOT NULL,
  feature_value DOUBLE,
  dt DATE NOT NULL,
  PRIMARY KEY (user_id, feature_name, dt)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
避坑提醒:HBase 的 RowKey 设计不当会导致 Region 热点,写入性能急剧下降。建议对 user_id 做散列(如 MD5 取前 4 位)作为前缀。MySQL 存储特征时,避免单表行数超过 5000 万,需提前规划分表策略。

4.4 离线特征管线整体流程

下图展示了从数据源接入、特征计算到特征存储的完整离线链路,以及各环节的关键组件。

离线特征工程管线流程 数据源接入 Hive 表 / HDFS 文件 特征计算 Spark SQL / DataFrame 特征存储 HBase / MySQL 接入细节 • 分区发现:dt/hour 分区 • 格式支持:Parquet/ORC/Text • 读取优化:谓词下推 计算细节 • 聚合统计:COUNT/SUM/AVG • 窗口函数:LAG/ROW_NUMBER • 多表 Join:Broadcast/SortMerge 存储细节 • HBase:RowKey 散列 • MySQL:唯一索引 • 批量写入:BulkLoad/Batch 调度与监控(Airflow / DolphinScheduler) 特征输出:离线特征表 / 在线缓存

4.5 本章小结

  • 数据源接入:Hive 表优先使用分区裁剪,HDFS 文件需指定 schema 并做预处理。
  • 特征计算:Spark SQL 支持聚合、窗口、Join 等丰富语义,注意数据倾斜和 Shuffle 优化。
  • 特征存储:HBase 适合高并发随机读取,RowKey 设计是关键;MySQL 适合结构化查询,注意索引和分表。
  • 管线调度:使用 Airflow 或 DolphinScheduler 编排离线任务,保障数据产出的时效性和稳定性。
课后练习:请设计一个用户近 30 天购买金额特征的离线计算任务,数据源为 Hive 表 orders(分区字段 dt),将计算结果写入 HBase 表 user_purchase_features,RowKey 为 user_id 的 MD5 前缀 + user_id。尝试写出完整的 Spark SQL 和 HBase 写入代码框架。