3、特征存储设计:Redis、HBase、Cassandra 在实时特征中的存储模式与选型
实时特征计算出来之后,放哪儿?
这个问题,我当年刚做实时特征的时候,觉得很简单——不就是个缓存吗?Redis 一把梭。后来线上出了几次事故,我才意识到,特征存储的选型,直接决定了整个实时特征系统的可用性、成本和运维复杂度。
今天我们就来聊聊三种主流存储:Redis、HBase、Cassandra。它们各自适合什么场景?怎么用?坑在哪儿?
核心观点:没有银弹。特征存储选型取决于三个维度——QPS 高低、数据量大小、一致性要求。选对了,事半功倍;选错了,天天救火。
3.1 Redis:实时特征的"快枪手"
Redis 是我用得最多的特征存储。为什么?快。单机 QPS 能到 10 万+,延迟在毫秒级。对于实时推荐、实时广告这类场景,Redis 几乎是标配。
但 Redis 不是万能的。我见过很多团队把 Redis 当数据库用,结果内存爆了、数据丢了、主从切换卡了。说白了,Redis 是缓存,不是持久化存储。
Redis 在实时特征中的典型模式
- Hash 结构:存储用户画像特征。key 是 user_id,field 是特征名,value 是特征值。适合批量读取。
- Sorted Set:存储实时排行榜特征。比如商品热度分,用 score 排序。
- String + 过期时间:存储临时特征,比如 session 级别的行为特征,TTL 设成 30 分钟。
- Bitmap:存储用户是否点击、是否曝光等布尔特征,内存占用极小。
// 用户画像特征写入示例
// key: user:profile:{user_id}
// field: age, gender, city, ltv
// value: 对应的特征值
// 写入
HSET user:profile:10001 age 28 gender male city beijing ltv 3.5
// 批量读取
HMGET user:profile:10001 age gender city
// 返回: ["28", "male", "beijing"]
// 设置过期时间,防止僵尸数据
EXPIRE user:profile:10001 86400 // 24小时过期
我的经验:Redis 的过期策略一定要用好。我曾经在一个项目里没设过期时间,结果用户画像越积越多,内存从 8G 涨到 32G,最后 OOM 了。现在我的习惯是:所有特征数据都设 TTL,除非你明确知道它需要永久保留。
Redis 的坑
- 内存有限:数据量超过内存容量,要么扩容,要么淘汰。我建议单机不超过 32G。
- 持久化弱:RDB 和 AOF 都有丢数据的风险。别把 Redis 当唯一存储。
- 大 key 问题:一个 Hash 里有几百万个 field,会导致阻塞。我踩过这个坑,后来用拆分 key 解决。
3.2 HBase:强一致性的"老大哥"
HBase 适合什么场景?数据量大、需要强一致性、对延迟要求不那么苛刻的场景。
我记得有一次做风控特征存储,要求特征数据必须严格一致——用户的行为一旦写入,必须立刻能被读到,不能有延迟。Redis 的主从异步复制满足不了,最后选了 HBase。
HBase 在实时特征中的存储模式
- Rowkey 设计:通常是 user_id + 时间戳的反转,保证热点分散。
- 列族设计:按特征类型分列族,比如 cf_action(行为特征)、cf_profile(画像特征)。
- 版本控制:HBase 天然支持多版本,适合存储特征的历史变化。
// HBase 特征存储 Rowkey 设计
// Rowkey: user_id(4字节) + reverse_timestamp(8字节)
// 例如: 10001_18446744073709551615
// 写入特征
put 'feature:user', '10001_18446744073709551615', 'cf_action:click_cnt', '15'
put 'feature:user', '10001_18446744073709551615', 'cf_action:view_cnt', '32'
// 读取最新特征
get 'feature:user', '10001_18446744073709551615'
// 扫描某个时间范围的特征
scan 'feature:user', {STARTROW => '10001_18446744073709550000',
STOPROW => '10001_18446744073709551615'}
注意:HBase 的 Rowkey 设计是重中之重。设计不好,就会出现 Region 热点——所有请求打到一个 Region Server 上。我见过一个团队把时间戳放 Rowkey 前缀,结果每天凌晨写入量暴增,Region Server 直接挂掉。Rowkey 一定要加盐(salting)。
HBase 的适用场景
- 风控特征:需要强一致性,不能容忍数据不一致。
- 离线+实时混合:HBase 可以同时支撑批量导入和实时写入。
- 特征历史回溯:多版本特性方便做特征回滚和调试。
3.3 Cassandra:高可用的"分布式战士"
Cassandra 是我后来才深入使用的。说实话,一开始我对它有点偏见——觉得它太复杂。但后来做跨机房部署的特征存储时,Cassandra 的优势就体现出来了。
Cassandra 没有单点故障,支持多数据中心复制,写入性能极好。适合写多读少、对可用性要求极高的场景。
Cassandra 在实时特征中的存储模式
- Primary Key 设计:Partition Key 决定数据分布,Clustering Key 决定排序。
- TTL 自动过期:Cassandra 支持表级别和列级别的 TTL,非常适合临时特征。
- 轻量级事务:通过 IF NOT EXISTS 实现去重写入。
// Cassandra 建表语句
CREATE TABLE feature.user_features (
user_id text,
feature_name text,
feature_value text,
update_time timestamp,
PRIMARY KEY (user_id, feature_name)
) WITH default_time_to_live = 86400; // 24小时自动过期
// 写入特征
INSERT INTO feature.user_features (user_id, feature_name, feature_value, update_time)
VALUES ('10001', 'click_cnt_1h', '15', toTimestamp(now()));
// 读取某个用户的所有特征
SELECT * FROM feature.user_features WHERE user_id = '10001';
// 读取特定特征
SELECT feature_value FROM feature.user_features
WHERE user_id = '10001' AND feature_name = 'click_cnt_1h';
我的建议:Cassandra 的 TTL 机制非常实用。实时特征很多都是有时效性的——比如"过去1小时点击次数",过了1小时就没意义了。用 TTL 自动清理,省去了写定时任务的麻烦。
Cassandra 的坑
- 读性能不如写性能:Cassandra 是写优化的,读路径比较重。如果读 QPS 很高,需要加缓存层。
- 二级索引性能差:别在 Cassandra 里做复杂查询,它只适合按主键查询。
- 数据模型设计要一次到位:Cassandra 不支持表结构变更,设计错了就得重建表。
3.4 选型对比:一张表说清楚
| 维度 | Redis | HBase | Cassandra |
|---|---|---|---|
| 延迟 | 毫秒级(1-5ms) | 10-50ms | 5-20ms |
| QPS | 10万+ | 1-5万 | 5-10万 |
| 数据量 | GB ~ 几十GB | TB ~ PB | TB ~ PB |
| 一致性 | 最终一致性 | 强一致性 | 可调一致性 |
| 持久化 | 弱(RDB/AOF) | 强(HDFS) | 强(SSTable) |
| 运维复杂度 | 低 | 高 | 中 |
| 典型场景 | 用户画像、实时计数 | 风控特征、离线+实时混合 | 跨机房、写多读少 |
3.5 我的选型建议
说了这么多,到底怎么选?我总结了一个简单的判断逻辑:
- 先看数据量:如果数据量在几十 GB 以内,无脑选 Redis。简单、快、好维护。
- 再看一致性要求:如果必须强一致,选 HBase。别用 Redis 做强一致场景,你会后悔的。
- 最后看可用性要求:如果要求 99.99% 可用,选 Cassandra。它的去中心化架构天生高可用。
一个真实案例:我之前做的一个实时推荐系统,用户画像特征用 Redis(数据量 20GB,QPS 8万),行为序列特征用 HBase(数据量 2TB,需要强一致),跨机房同步的特征用 Cassandra(多数据中心复制)。三种存储各司其职,系统跑了两年没出过大问题。
嗯,特征存储设计就聊到这儿。记住一句话:没有最好的存储,只有最合适的存储。根据你的场景选,别跟风。