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 自动迁移策略
- 时间触发迁移:每日凌晨执行定时任务,将超过 7 天的特征从 Redis 导出到 ClickHouse,超过 30 天的从 ClickHouse 归档到 Hive。
- 访问频率触发迁移:在 Redis 中记录特征访问次数,连续 3 天访问次数低于阈值(如 100 次/天)的特征自动降级为温数据。
- 双写 + 异步删除:写入时同时写入热存储与温存储,热存储数据过期后仅删除热存储副本,温存储保留完整历史。
# 冷热迁移伪代码(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 特征存储架构总览
下图展示了在线特征存储、离线特征存储与冷热分离的整体架构,以及数据在三个层级之间的流转路径。
4.5 本章小结
- 在线特征存储:Redis 适合小数据量、低延迟场景;HBase 适合大数据量、宽表模型场景。两者都需要精心设计 Key 结构与过期策略。
- 离线特征存储:Hive 是 ETL 与回溯的主力,ClickHouse 是实时分析与监控的利器。合理使用分区、分桶与物化视图可大幅提升查询性能。
- 冷热分离:按时间与访问频率将特征划分为热、温、冷三层,通过自动迁移与回填机制平衡成本与性能。这是大规模特征工程降本增效的核心手段。