3、数据采集层:消息队列选型与数据格式设计
数据采集层是整个实时特征计算的入口。说白了,这一层要是没选好,后面所有环节都得跟着遭殃。我见过太多团队在消息队列上栽跟头——要么吞吐上不去,要么数据丢了找不回来。
今天咱们就聊聊三个核心问题:消息队列选Kafka还是Pulsar、数据格式用Avro还是Protobuf、分区与副本策略怎么定。
3.1 消息队列选型:Kafka vs Pulsar
先说说我的个人习惯。做实时特征计算,消息队列选型我一般看三个维度:吞吐能力、数据可靠性、运维复杂度。
Kafka 是业界老牌选手。它的架构设计很纯粹——分区日志模型,每个分区追加写入,顺序读取。吞吐量极高,单机轻松跑几十万条/秒。我曾在某电商大促场景中,用Kafka扛过每秒120万条的用户点击流,稳得很。
但Kafka有个痛点:分区数不能太多。分区多了,ZK压力大,Rebalance慢,集群抖动明显。我建议单集群分区数控制在2000以内,超过这个数就得考虑拆分集群了。
Pulsar 是后起之秀。它把存储和计算分离了——Broker只管计算,BookKeeper管存储。好处是分区数可以轻松上万,扩容也方便,加Broker就行,不用搬数据。
Pulsar还有个杀手锏:分层存储。老数据自动转存到S3或HDFS,本地只保留热数据。这对实时特征计算来说太实用了——特征回溯时,直接从冷存储读历史数据,不用提前预写大量日志。
不过Pulsar的运维复杂度比Kafka高。BookKeeper集群需要单独维护,网络延迟敏感,部署调优门槛不低。我建议团队规模在10人以上再考虑Pulsar。
选型建议:
- 团队小、场景简单 → Kafka(运维省心,生态成熟)
- 分区数多、需要分层存储 → Pulsar(弹性好,冷热分离)
- 对数据一致性要求极高 → Pulsar(ACK机制更精细)
我的经验:如果你们团队之前没用过消息队列,我建议从Kafka入手。踩坑少,社区活跃,遇到问题百度一下就有答案。Pulsar虽然好,但坑也多,我曾经因为BookKeeper的磁盘IO配置不当,导致写入延迟飙升了3倍。
3.2 数据格式设计:Avro vs Protobuf
数据格式这事,很多人不重视。觉得不就是序列化嘛,JSON也能用。嗯,这里要注意——实时特征计算的数据量极大,格式选不好,带宽和CPU都扛不住。
我主要对比两个格式:Avro 和 Protobuf。
| 对比维度 | Avro | Protobuf |
|---|---|---|
| 序列化方式 | 二进制,自带Schema | 二进制,Schema单独定义 |
| Schema演进 | 支持向前/向后兼容 | 支持,但需手动管理字段编号 |
| 压缩率 | 高(无冗余字段名) | 高(字段用编号代替) |
| 解析速度 | 快(无需加载Schema) | 快(但需预编译) |
| 生态集成 | Kafka原生支持 | 通用性好,多语言支持 |
| 学习成本 | 低(Schema定义直观) | 中(需理解.proto文件) |
我个人更倾向Avro,尤其是在Kafka场景下。为什么?
- Avro的Schema是内嵌在数据文件里的,消费者拿到数据就能解析,不需要额外维护Schema Registry(虽然建议用)。
- Avro支持Schema兼容性检查——新增字段时,老消费者不会报错,只会忽略未知字段。这在特征计算场景太重要了,你想想看,特征字段经常要加,总不能每次加字段都重启所有消费者吧?
Protobuf也有优势:序列化后的体积更小,解析速度更快。但它的Schema管理比较麻烦——字段编号不能重复,删除字段要标记reserved,否则容易出兼容性问题。我曾经在项目中因为Protobuf字段编号冲突,导致线上数据解析失败,排查了整整一天。
避坑指南:
- 不要用JSON做实时特征计算的消息格式。体积大、解析慢、Schema不明确,后期维护成本极高。
- 如果选Avro,一定要配合Schema Registry使用。否则Schema变更后,历史数据无法解析。
- 如果选Protobuf,字段编号从1开始,预留足够的编号范围(建议1-100给核心字段,100+给扩展字段)。
3.3 分区与副本策略
分区和副本,是消息队列性能和数据可靠性的基石。这块要是设计不好,后面怎么调都白搭。
3.3.1 分区策略
分区的核心作用:并行消费。每个分区对应一个消费者线程,分区数越多,消费能力越强。
但分区数不是越多越好。我见过有人把Kafka分区设到1000个,结果消费者Rebalance花了5分钟,期间数据全积压。为什么会这样?因为每个分区都要分配消费者,分区越多,Rebalance的计算量越大。
分区数计算公式(我自己的经验):
分区数 = 目标吞吐量 / 单分区吞吐量 × 1.5(预留缓冲)
举例:
目标吞吐量 = 100万条/秒
单分区吞吐量 = 5万条/秒(一般Kafka单分区能扛3-8万)
分区数 = 100 / 5 × 1.5 = 30个
注意:分区数最好设置成消费者数量的整数倍。比如你有5个消费者,分区数设30,每个消费者处理6个分区,负载均衡。
3.3.2 副本策略
副本的作用:保证数据不丢。Kafka和Pulsar都支持副本机制,但配置方式不同。
Kafka副本策略:
- 副本因子(replication.factor)建议设为3。Leader副本负责读写,Follower副本同步数据。
- ISR(In-Sync Replicas)机制:只有同步的副本才能参与Leader选举。我建议min.insync.replicas设为2,这样至少有一个Follower同步了数据,Leader挂了也能快速切换。
- ACKS配置:acks=all(最安全),acks=1(性能与安全平衡),acks=0(性能最高,但可能丢数据)。
Pulsar副本策略:
- BookKeeper的Ensemble Size(E)和Write Quorum(Q)和Ack Quorum(A)三个参数控制副本。
- 我常用的配置:E=3, Q=3, A=2。意思是数据写入3个节点,只要2个节点确认就算写入成功。
- Pulsar的副本是条带化存储的——数据分片后分散到多个BookKeeper节点,不像Kafka那样整个分区存在一个节点上。好处是单节点故障影响范围小,坏处是网络开销大。
副本配置建议:
- 生产环境:副本因子=3,min.insync.replicas=2,acks=all
- 测试环境:副本因子=2,min.insync.replicas=1,acks=1
- 单节点环境:副本因子=1(不推荐,数据无保障)
3.4 整体架构图
下面这张图展示了数据采集层的核心组件和流程。我把它画成了SVG,方便你理解各模块之间的关系。
3.5 实战避坑总结
最后,分享几个我踩过的坑,希望能帮你少走弯路。
我曾经踩过的坑:
- 分区数设太多导致Rebalance超时:某次我把Kafka分区数设到500个,消费者组一重启,Rebalance花了8分钟,期间数据积压了上亿条。后来我把分区数降到100个,Rebalance时间控制在30秒内。
- 副本因子设太低导致数据丢失:测试环境副本因子设了1,结果单节点磁盘坏了,数据全丢。从那以后,我所有环境至少设2个副本。
- Avro Schema不兼容导致消费失败:有一次我删除了Avro Schema中的一个字段,老消费者直接报错。后来我学会了用Schema Registry的兼容性检查,新增字段用默认值,删除字段用deprecated标记。
数据采集层是实时特征计算的起点,选型对了,后面事半功倍。我个人建议:Kafka + Avro + 3副本 + 合理分区,这套组合能覆盖90%的场景。如果你有特殊需求,比如分区数超过2000,或者需要冷热数据分离,再考虑Pulsar。