Kafka的悖论:当实时数据基础设施成为遗留系统
Kafka早已不是新兴技术,它已经走过了十三个年头。在绝大多数技术团队的架构图里,Kafka与Zookeeper(或KRaft)被画在最底层,像水电一样理所当然。然而,我们是否正在把Kafka的“够用”误当成“最优解”?这篇文章想提出一个有些逆耳的观点:Kafka正在成为新一代的“遗留系统”——不是因为它的性能衰退了,而是因为我们赋予了它超出其设计哲学的责任。当事件流、数据库、消息队列、流处理引擎的边界被刻意模糊时,Kafka的原始设计——一个分区的、追加日志型的消息总线——开始与后现代数据应用的诉求产生结构性摩擦。
这种摩擦最明显的表征是“重消费者”模式的普及。Kafka的消费者模型假设消息消费后可丢弃,状态由外部系统管理。但今天,开发者却越来越多地利用Kafka实现状态化服务:例如通过Kafka Streams或KSQL做窗口聚合、用紧凑日志模拟状态存储,甚至有人把Kafka当作不可变数据库来维护业务真源。这并非错误,却是一种“越权”。Kafka的存储设计虽然持久,但并非为主数据服务的,它的数据保留策略、分区键分布、消费者重平衡机制,都会在高吞吐+长周期保存+精确一次语义的三重压力下变得异常脆弱。相比之下,Apache Pulsar在架构上把存储和计算分离,用BookKeeper作为独立存储层,支持更灵活的数据生命周期管理——但Pulsar的采用率远不及Kafka,原因并非技术优劣,而是生态惯性和运维心智的锁定。
更深层的对比在于“事件流”与“流处理”的混淆。Kafka是一个出色的传输层,它提供的是事件的暂时缓冲区;而流处理(如Flink/Spark Streaming)需要的是连续的状态计算和跨事件的时间窗口语义。Kafka在传输层做得越极致,就越容易让开发者产生一种幻觉:只要把消息推进Kafka,流处理就自然完成了。这种幻觉导致了大量以Kafka为中心的“伪流处理”系统——它们消费消息,批处理式地写入数据库,再开启新任务扫描,形成了一个个微批循环。本质上,这种架构没有摆脱批处理的时间心智,却牺牲了Kafka本可带来的实时性优势。同时,Kafka的消费者组机制在动态扩缩容时会产生Stop-the-World式的重平衡,这对毫秒级SLA的线上服务是一种隐患。而Pulsar通过分离式订阅模型,以Cursor和独立Broker摆脱了这种“群体协作”的阻塞,尽管它在端到端延迟和吞吐微基准上未必击败Kafka。
我认为,行业正在进入一个“后Kafka”的共识阶段:我们需要的不只是一个消息管道,而是一个能承载完整事件生命周期、支持多模态订阅、并原生集成有限状态计算的数据基础设施。Kafka并非会消失,它会退居为一种标准协议——就像TCP/IP一样,成为底层传输事实,但上层应用将不再直接面对它。Confluent正在向“数据流平台”转型,而Pulsar和Redpanda则用不同的设计哲学去攻击Kafka的边界。Redpanda用C++重写并内置了Raft,把Kafka的协议兼容变成了一个纯内核事件循环;Pulsar用分层存储和集成的函数计算模糊了消息与流处理的边界。这些替代者未必更“好”,但它们的兴起揭示了Kafka生态中一个长期被忽略的结构性缺陷:存储与计算耦合太深,客户端协议太重,运维心智依赖外部协调者。
我们不应把Kafka当作答案,而应把它当作一个时代的坐标。如果实时数据基础设施的真正目标,是让事件在产生后能被任意时间、任意速度、任意形式的消费者所使用,那么Kafka的分区追加日志模型只是其中的一个切片。未来架构可能由多引擎协同:Kafka负责高吞吐的短期事件流,Pulsar负责多租户的长期事件存储,Flink负责无界状态计算,而数据库本身成为事件溯源所投射的最终视图。在这个图景里,Kafka的“分区且顺序”反而成了一种限制——尤其当我们需要对相同事件进行跨主题的关联分析时,Kafka的拓扑蔓延会让运维变得笨重。
所以,我的独立观点是:不要继续在Kafka上做加法,而要在它的外围建立适配层和流量管理器。将Kafka视作一种可替换实现,而不是不可动摇的底层。标准化的API(如AsyncAPI)、多语言客户端、以及更轻量的替代实现,都在降低迁移成本。真正成熟的架构应当允许你的核心中间件在某个时期“离开Kafka”,就像当年我们离开Oracle一样——不是因为它不够好,而是因为它不再是你业务模型的精确映射。最终,事件流的基础设施将走向“功能极化”:消息只负责移动,状态存储交给数据库,计算交给流引擎。Kafka的悲剧性荣光在于,它曾同时扮演了所有角色,而如今我们必须把一个复杂角色分配回多个简单系统。这正是架构演进的本意:让每个组件恢复纯粹的职责。
在实践层面,团队在审视自身的Kafka使用时,应当问一个问题:你的Kafka集群里,有多少值是直接被消费并立即丢弃的?如果超过90%的消息在经过一次消费后就被删除,那么你的Kafka本质上是一个昂贵的数据丢弃管道——用三副本和ISR机制去保护不会重读的数据,这既是性能浪费,也是设计错位。反之,如果你的Kafka消息会被多次重放、长时间保留,那么你应该检查存储成本、压缩策略以及消费者隔离性。我见过不少团队把主题的分区数设定成恒定的,却不知道KRaft模式下的分区规模扩展仍然是一项不可逆操作。还有团队把多个业务域塞进同一个集群,导致噪音邻居和慢消费者互相干扰——这些都是因为把Kafka当作无所不能的中间件,而忘记了它只是一个带缓冲的提交日志。
成熟的架构师不会拒绝Kafka,但会把Kafka钉在“事件流水线”这一个固定位置上。事件流水线意味着它不承诺状态,不承诺查询,不承诺跨源关联。它只需要做到快速、可靠、顺序地让消息从A移动到B。反之,那些需要状态、查询和关联的需求,应该由流处理引擎和数据库在Kafka的下游去承接。如果你正经历Kafka消费端状态的频繁丢失,或者因为重平衡导致的故障,不要升级版本、调整参数——先检查你的系统设计是不是让Kafka做了它不该做的事。当你能把“真实时”和“近似实时”分清楚,把“消息总线”和“数据平台”分清楚,你就摆脱了Kafka既定叙事的束缚。
正如数据库从层次模型进化到关系模型,消息系统也从发布订阅进化到了事件流平台。Kafka代表的是第一代真正意义上的事件流平台,但平台不等于终点。如果我们把时间拉长到五年后,Kafka极有可能像当年的EMC存储阵列一样,仍然出现在企业的机房里,但不再是数字化转型演讲中的核心。真正被铭记的,不是Kafka本身,而是它所催生的“事件驱动”思考方式——那才是这个时代最珍贵的遗产。面对这个遗产,我们不只要使用它,更要批判它、超越它。让Kafka回归工具,让我们重新掌舵意义。