Kafka消息应发送实体全量数据还是仅发送变更字段?
先明确前提场景:假设有如下结构的实体数据:
{ id: 12, name: 'bob', address: { street: 'main', code:1234 } }
当该实体address下的street字段更新为new street时,生产者发布消息通常有两种可选实现:
- 发送包含所有字段的全量实体文档:
{ id: 12, name: 'bob', address: { street: 'new street', code:1234 } }
- 仅发送本次发生变更的字段内容:
{ id: 12, address: { street: 'new street' } }
首先说结论:两种格式都是技术层面完全允许的实现,不存在"Kafka官方规定必须用某一种"的说法。
很多人会纠结Kafka的事件语义到底应该对标HTTP的PUT全量替换,还是PATCH增量更新——实际上这个问题从Kafka本身的设计里找不到标准答案,Kafka本质就是个可持久化的分布式append-only消息日志,只负责把生产者发的字节数组按顺序投递给消费者,根本不关心消息体里存的是全量还是增量内容,所谓的事件语义完全是业务层自己定义的规则,和Kafka本身没关系。
不少开发者倾向于选仅发变更字段的方案,核心原因确实和Kafka的机制有关:Kafka默认单条消息大小上限是1MB,就算集群调优一般也不会把这个阈值设得特别高,如果业务里的单实体体量很大(比如包含长文本、嵌套多层的大对象,单条实体大小能到几百KB甚至数MB),每次只改一个小字段就发全量,会平白浪费生产端到Broker的带宽、占Broker的存储资源,消费者拉取消息的开销也会变高,这种场景下发增量字段的投入产出比很高。但选增量方案一定要把配套逻辑补全:必须保证消费者端能可靠获取到实体的基准状态做字段合并,还要额外处理消息乱序、字段删除、嵌套对象变更等边界场景——比如这次更新除了改street,还把code字段删掉了,只发street的变更的话,消费者根本感知不到code字段需要删除,很容易生成脏数据。
全量发送的方案胜在逻辑足够简单:消费者拿到消息之后,直接用消息里的实体内容覆盖本地存储的对应id的数据就行,不需要做任何字段合并操作,也不用额外处理乱序、漏字段的问题,排查问题的时候单拎出一条消息就能看到事件发生时刻实体的完整状态,不需要回溯历史上的所有变更消息拼状态,运维和排障的成本低很多。对于绝大多数单实体体量不大(比如单条实体大小在几KB以内)的业务场景,全量发送是综合成本更低、落地更稳妥的选择,也是目前行业内更通用的实践。
最后提个实在的建议:别搞教条主义,不用硬套网上所谓的"最佳实践"。如果你的业务里实体本身很小,全量发送根本碰不到Kafka的消息大小阈值,就没必要折腾增量方案那套复杂的合并逻辑,给自己找不必要的维护成本;如果你的单实体确实大到全量发送会频繁触发大小限制、占满集群带宽,那再考虑增量方案也不迟。
内容的提问来源于stack exchange,提问作者user3579222

