架构设计原则:实时特征平台的骨架
做实时特征平台,说白了就是在跟时间赛跑。数据从产生到被消费,中间每一毫秒的延迟都可能让特征失效。我见过不少团队,一上来就堆组件,Kafka、Flink、Redis、HBase 全上,结果跑起来发现要么延迟爆炸,要么数据对不上。嗯,这里的关键不是用了什么技术,而是架构设计有没有想清楚。
我个人习惯,在画第一版架构图之前,先问自己三个问题:
- 数据从哪里来,到哪里去?
- 每一层承担什么职责?
- 挂了怎么办?
想明白这三点,架构就不会跑偏。
总体架构:四层各司其职
实时特征平台的架构,我把它拆成四层:接入层、计算层、存储层、服务层。每一层只做自己的事,层与层之间通过标准接口通信。这样做的好处是,哪一层出了问题,替换起来不伤筋动骨。
核心原则:层间解耦,数据不丢,延迟可控。
下面这张图是我在项目中常用的分层结构,你可以先有个整体印象:
接入层:数据的第一道关卡
接入层是数据的入口。我建议用消息队列做缓冲,而不是直接让业务系统往计算层怼数据。为什么?因为业务系统的流量是突发的,计算层扛不住毛刺。
我在项目中遇到过这样的情况:双十一大促,业务方直接把 HTTP 请求打到 Flink 上,结果 Flink 背压飙到 100%,整个作业挂了。后来改成 Kafka 接入,流量削峰填谷,稳得很。
我的经验:接入层至少要做到三点——数据不丢(ACK 机制)、顺序保障(分区键设计)、流量控制(限流/降级)。
接入层常用的技术选型:
| 组件 | 适用场景 | 注意事项 |
|---|---|---|
| Kafka | 高吞吐、持久化、多消费者 | 分区数要提前规划,后期改分区很麻烦 |
| Pulsar | 多租户、延迟敏感、存储计算分离 | 运维成本比 Kafka 高一些 |
| HTTP Gateway | 低频写入、调试、管理操作 | 不适合高并发实时写入 |
计算层:特征加工的核心
计算层是实时特征平台的大脑。数据进来之后,要在这里完成清洗、聚合、关联、窗口计算等一系列操作。
我个人习惯把计算逻辑分成三类:
- 无状态计算:比如字段映射、格式转换,每条数据独立处理。
- 有状态计算:比如滑动窗口内的均值、计数,需要维护中间状态。
- 多流关联:比如把用户点击流和订单流关联起来,生成完整特征。
你想想看,如果所有逻辑都揉在一个 Flink 作业里,调试和维护会多痛苦。我建议按业务域拆分作业,每个作业只做一件事。比如用户画像一个作业,商品特征一个作业,互不干扰。
避坑指南:我曾经把一个复杂的多流关联逻辑写在一个大作业里,结果状态后端频繁 OOM。后来拆成三个小作业,每个作业只维护自己的状态,问题就解决了。记住,状态是计算层的命门,一定要控制好状态大小。
存储层:特征数据的归宿
计算层产出的特征,最终要落到存储层。存储层的设计直接影响特征查询的延迟和吞吐。
我常用的存储选型策略:
| 存储类型 | 典型场景 | 延迟要求 |
|---|---|---|
| Redis | 在线特征查询,毫秒级响应 | < 5ms |
| HBase | 大吞吐写入,历史特征回溯 | < 50ms |
| ClickHouse | OLAP 分析,批量特征计算 | < 200ms |
| 对象存储 | 冷数据归档,离线训练 | 秒级 |
这里有个关键点:特征数据是有时效性的。比如用户最近 5 分钟的点击行为,过了这个窗口就没用了。我建议在存储层设置 TTL(生存时间),自动清理过期数据,避免存储爆炸。
核心原则:热数据用内存存储(Redis),温数据用 SSD 存储(HBase),冷数据用廉价存储(对象存储)。分层存储能大幅降低成本。
服务层:对外提供特征
服务层是特征平台的出口。模型推理服务、在线推荐系统、实时风控引擎,都通过服务层获取特征。
我建议服务层提供两种接口:
- 单特征查询:根据实体 ID 和特征名,返回最新值。延迟要求 < 10ms。
- 批量特征查询:一次请求获取多个实体的多个特征。延迟要求 < 50ms。
服务层还要做好缓存。我见过一个项目,每次请求都穿透到 Redis,结果 Redis 连接数被打满。后来加了一层本地缓存(Caffeine),命中率 80% 以上,Redis 的压力瞬间降下来了。
我的经验:服务层一定要做降级和熔断。如果存储层挂了,服务层可以返回缓存中的旧特征,或者返回默认值。宁可给一个旧值,也不能让上游服务超时。
高可用与容灾设计
实时特征平台最怕什么?最怕数据断流。一旦特征数据中断,模型推理结果就是错的,业务损失不可估量。
我总结了几条高可用设计原则:
- 接入层高可用:Kafka 集群至少 3 个 Broker,副本因子设为 3。这样坏掉一台,数据不丢。
- 计算层高可用:Flink 开启 Checkpoint,状态后端用 RocksDB,存储到 HDFS 或 S3。作业挂了可以从 Checkpoint 恢复。
- 存储层高可用:Redis 用哨兵或集群模式,HBase 用 Region 副本。主从切换要自动完成。
- 服务层高可用:多副本部署,前面挂负载均衡。健康检查要检测特征查询的延迟,而不是只检测端口是否存活。
容灾设计方面,我建议做异地多活。比如主集群在北京,备集群在上海。正常情况下流量走北京,北京挂了自动切到上海。数据同步用 Kafka MirrorMaker 或者 Flink 双写。
避坑指南:我曾经做过一个容灾方案,只做了存储层的异地备份,没考虑计算层的状态同步。结果主集群挂了,备集群启动后,特征数据从备份恢复,但 Flink 的状态是空的,导致窗口计算全部出错。后来我改成计算层状态也做异地备份,才彻底解决问题。
最后,容灾方案一定要定期演练。我见过不少团队,容灾方案写得漂漂亮亮,但从来没演练过。真到出问题的时候,才发现切换脚本有 bug,或者权限没配好。嗯,这里要记住:不演练的容灾方案,等于没有。
总结一下:实时特征平台的架构设计,核心是分层、解耦、容灾。每一层做好自己的事,层与层之间通过标准接口通信。高可用不是靠一个组件,而是靠整个链路的设计。你想想看,如果每一层都做了冗余和容错,平台还会轻易挂掉吗?