4. 特征存储设计:在线特征存储(Redis/HBase)、离线特征存储(Hive/ClickHouse)、冷热数据分离策略

特征存储是规则引擎与风控系统的数据中枢。在线特征要求毫秒级响应,离线特征要求海量吞吐与灵活分析。本章围绕在线/离线存储选型、数据模型差异与冷热分离策略展开,帮助你在不同延迟与成本约束下做出合理设计。

4.1 在线特征存储:Redis 与 HBase

在线特征存储服务于实时规则计算,核心指标为 P99 延迟 ≤ 10ms、QPS ≥ 10 万、数据一致性。主流方案为 Redis 与 HBase,两者在数据结构、持久化与扩展性上存在显著差异。

维度 Redis HBase
数据模型 Key-Value / Hash / Sorted Set 宽表(RowKey + 列族 + 时间戳)
延迟 1-3 ms(纯内存) 5-15 ms(依赖 HDFS 与 RegionServer)
持久化 AOF / RDB(异步刷盘) WAL + HFile(强持久化)
扩展性 主从 + 集群(分片槽 16384) 原生分布式(Region 自动分裂)
典型场景 设备指纹、用户画像、计数器 行为序列、关联图谱、历史特征
成本 高(全内存) 中(内存 + 磁盘)
选型建议:若特征数据量 < 100GB 且对延迟极度敏感(如实时反欺诈),优先 Redis。若特征数据量 > 1TB 且需要存储用户行为序列(如 90 天交易记录),HBase 的宽表模型与自动分裂更合适。

4.1.1 Redis 特征存储设计要点

  • Key 命名规范:feature:{entity_type}:{entity_id}:{feature_name},例如 feature:user:12345:credit_score。
  • Hash 结构:将同一实体的多个特征聚合到一个 Hash Key 中,减少连接数与内存碎片。
  • TTL 策略:在线特征通常设置 1-24 小时过期,避免冷数据占用内存。
  • 缓存穿透防护:对空值特征设置短 TTL(如 30 秒)或使用布隆过滤器。
# Redis Hash 特征写入示例(Python)
import redis
r = redis.Redis(host='localhost', port=6379, decode_responses=True)

# 写入用户特征
r.hset('feature:user:12345', mapping={
    'credit_score': 720,
    'age': 35,
    'device_count': 2
})
# 设置 TTL 为 2 小时
r.expire('feature:user:12345', 7200)

# 读取单个特征
score = r.hget('feature:user:12345', 'credit_score')

4.1.2 HBase 特征存储设计要点

  • RowKey 设计:采用 entity_type + 倒序时间戳 或 entity_id + 时间戳,确保热点分散与范围扫描效率。
  • 列族划分:按特征类别分列族,例如 cf_profile(静态画像)、cf_behavior(行为序列)、cf_risk(风险标签)。
  • 版本控制:利用 HBase 的时间戳版本保留最近 N 次特征变更,支持回溯分析。
  • 预分区:根据 RowKey 哈希提前创建 10-50 个 Region,避免写入热点。
# HBase 特征写入示例(Java)
Configuration conf = HBaseConfiguration.create();
Connection connection = ConnectionFactory.createConnection(conf);
Table table = connection.getTable(TableName.valueOf("online_features"));

Put put = new Put(Bytes.toBytes("user_12345_20250315"));
put.addColumn(Bytes.toBytes("cf_profile"), Bytes.toBytes("credit_score"), Bytes.toBytes(720));
put.addColumn(Bytes.toBytes("cf_behavior"), Bytes.toBytes("last_login"), Bytes.toBytes("2025-03-15 10:30:00"));
table.put(put);
table.close();
在线特征存储核心原则:写路径尽量简单(避免 Join 与事务),读路径必须可预测(O(1) 或 O(log N)),内存/磁盘比例根据特征访问频率动态调整。

4.2 离线特征存储:Hive 与 ClickHouse

离线特征存储服务于样本生成、回溯分析、模型训练与批量特征计算。Hive 适合大规模 ETL 与复杂 SQL,ClickHouse 适合高并发分析查询与实时聚合。

维度 Hive ClickHouse
存储格式 ORC / Parquet(列存) MergeTree 系列(列存 + 稀疏索引)
查询延迟 秒级到分钟级(MapReduce/Spark) 毫秒到秒级(向量化执行)
数据更新 分区覆盖 / ACID(Hive 3+) Mutation 操作(异步重写)
并发能力 低(适合批处理) 高(适合分析型查询)
典型场景 全量特征计算、样本拼接、数据回溯 特征监控、实时聚合、Ad-hoc 分析
存储成本 低(HDFS 三副本) 中(本地 SSD + 压缩)

4.2.1 Hive 特征表设计规范

  • 分区策略:按日期分区(dt STRING),必要时增加二级分区(hour STRING)。
  • 分桶:按实体 ID 哈希分桶(如 64 桶),加速 Join 与采样。
  • 列裁剪:只存储特征 ID、特征值、时间戳、实体 ID,避免存储冗余元数据。
  • 压缩:使用 ZSTD 或 Snappy 压缩 ORC 文件,压缩比可达 5:1。
-- Hive 特征表 DDL 示例
CREATE TABLE IF NOT EXISTS offline_features (
    entity_id      STRING,
    entity_type    STRING,
    feature_name   STRING,
    feature_value  DOUBLE,
    event_time     TIMESTAMP
)
PARTITIONED BY (dt STRING)
CLUSTERED BY (entity_id) INTO 64 BUCKETS
STORED AS ORC
TBLPROPERTIES ('orc.compress'='ZSTD');

4.2.2 ClickHouse 特征表设计规范

  • 表引擎:使用 ReplicatedMergeTree 保证高可用,使用 SummingMergeTree 或 AggregatingMergeTree 预聚合特征。
  • 排序键:按 (entity_type, entity_id, event_time) 排序,加速范围查询。
  • TTL 策略:原始数据保留 30 天,聚合数据保留 90 天,过期自动删除。
  • 物化视图:预计算每日特征均值、最大值、最小值,减少查询计算量。
-- ClickHouse 特征表 DDL 示例
CREATE TABLE online_features.ck_features (
    entity_id      String,
    entity_type    String,
    feature_name   String,
    feature_value  Float64,
    event_time     DateTime
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/ck_features', '{replica}')
PARTITION BY toYYYYMMDD(event_time)
ORDER BY (entity_type, entity_id, event_time)
TTL event_time + INTERVAL 30 DAY;

-- 物化视图:每日特征均值
CREATE MATERIALIZED VIEW daily_feature_avg
ENGINE = SummingMergeTree()
PARTITION BY toYYYYMMDD(day)
ORDER BY (entity_type, feature_name)
AS SELECT
    entity_type,
    feature_name,
    toDate(event_time) AS day,
    avg(feature_value) AS avg_value
FROM ck_features
GROUP BY entity_type, feature_name, day;

4.3 冷热数据分离策略

特征数据具有明显的时间衰减特性:最近 7 天的特征贡献了 80% 以上的规则命中,而 30 天前的特征访问频率急剧下降。冷热分离的核心目标是 降低存储成本 而不牺牲在线查询性能。

常见误区:将所有特征都存入在线存储,导致内存成本爆炸。正确的做法是按特征访问频率与时效性划分冷热层级,并建立自动迁移机制。

4.3.1 冷热数据分层模型

层级 存储介质 数据范围 访问频率 延迟要求 压缩策略
热数据 Redis / 内存 最近 1-7 天 高(QPS > 10 万) < 5 ms 无压缩
温数据 ClickHouse / SSD 最近 8-30 天 中(QPS 1 千-1 万) < 100 ms LZ4 / ZSTD
冷数据 Hive / HDFS 30 天以上 低(偶尔回溯) 秒级到分钟级 ZSTD / Gzip

4.3.2 自动迁移策略

  1. 时间触发迁移:每日凌晨执行定时任务,将超过 7 天的特征从 Redis 导出到 ClickHouse,超过 30 天的从 ClickHouse 归档到 Hive。
  2. 访问频率触发迁移:在 Redis 中记录特征访问次数,连续 3 天访问次数低于阈值(如 100 次/天)的特征自动降级为温数据。
  3. 双写 + 异步删除:写入时同时写入热存储与温存储,热存储数据过期后仅删除热存储副本,温存储保留完整历史。
# 冷热迁移伪代码(Python)
def migrate_cold_features():
    # 1. 从 Redis 扫描所有特征 Key
    cursor = 0
    while True:
        cursor, keys = redis.scan(cursor, match='feature:*', count=1000)
        for key in keys:
            ttl = redis.ttl(key)
            if ttl < 0 or ttl > 604800:  # 超过 7 天未设置过期
                # 2. 读取特征值
                data = redis.hgetall(key)
                # 3. 写入 ClickHouse
                clickhouse.execute(
                    'INSERT INTO ck_features VALUES',
                    [(key, data['feature_value'], data['event_time'])]
                )
                # 4. 删除 Redis 中的冷特征
                redis.delete(key)
        if cursor == 0:
            break

4.3.3 冷数据查询加速

  • 预聚合:对冷数据按天、按实体类型预计算统计特征(均值、分位数、计数),避免全表扫描。
  • 索引表:在 Hive 中建立冷数据索引表,记录每个实体 ID 的数据所在分区与偏移量。
  • 缓存回填:当冷特征被规则命中时,自动回填到 Redis 热存储并设置短 TTL(如 1 小时),避免重复查询冷存储。
冷热分离收益估算:假设特征总量 10TB,热数据 500GB(5%),温数据 1.5TB(15%),冷数据 8TB(80%)。使用冷热分离后,内存成本降低 95%,总存储成本降低 60% 以上。

4.4 特征存储架构总览

下图展示了在线特征存储、离线特征存储与冷热分离的整体架构,以及数据在三个层级之间的流转路径。

特征存储冷热分离架构图 特征存储冷热分离架构 实时特征计算引擎 热存储:Redis 集群 最近 1-7 天 · P99 < 5ms · 全内存 每日 TTL 过期迁移 温存储:ClickHouse 最近 8-30 天 · 秒级查询 · SSD 30 天归档任务 冷存储:Hive / HDFS 30 天以上 · 分钟级查询 · ZSTD 压缩 规则引擎查询 热数据直连 温数据回填 关键设计要点: 1. 热存储只保留最近 7 天特征,通过 TTL 自动过期并迁移到温存储。 2. 温存储提供秒级查询能力,支持特征回填到热存储(缓存穿透保护)。 3. 冷存储使用列存 + 高压缩比,仅用于回溯分析与样本生成。

4.5 本章小结

  • 在线特征存储:Redis 适合小数据量、低延迟场景;HBase 适合大数据量、宽表模型场景。两者都需要精心设计 Key 结构与过期策略。
  • 离线特征存储:Hive 是 ETL 与回溯的主力,ClickHouse 是实时分析与监控的利器。合理使用分区、分桶与物化视图可大幅提升查询性能。
  • 冷热分离:按时间与访问频率将特征划分为热、温、冷三层,通过自动迁移与回填机制平衡成本与性能。这是大规模特征工程降本增效的核心手段。