Kafka 不只是个消息系统

  • Jay Kreps
  • 薛命灯

2017 年 10 月 29 日

话题:语言 & 开发架构

Confluent 联合创始人兼 CEO Jay Kreps 发表了一篇博文,给出了 Kafka 的真正定位——它不只是个消息系统,它还是个存储系统,而它的终极目标是要让流式处理成为现代企业的主流开发范式。以下内容翻译自作者的博文,查看原文It’s Okay To Store Data In Apache Kafka

人们总是问是否可以把 Kafka 作为长期的数据存储来使用,很显然,如果把数据保留策略设置为“永久”或者启用主题的日志压缩功能,那么数据就可以被永久保存下来。但我觉得人们其实真正想知道的是,这样做是不是很疯狂。

简而言之,这样做不算疯狂。实际上,人们一直都在这么做,而且 Kafka 的设计意图之一就是要将它作为数据存储系统。不过问题是,为什么我们要把 Kafka 作为数据存储呢?

  1. 你可能在构建一个基于事件溯源的应用程序,需要一个数据存储来保存变更日志。理论上,你可以使用任何一种存储系统。Kafka 已经解决了不可变(immutable)日志和基于这些日志生成“物化视图”的问题,既然这样,为什么不直接使用 Kafka 呢?纽约时报已经在他们的 CMS 系统里使用 Kafka 来保存他们的文章。
  2. 你可能在应用程序里使用了缓存,并从 Kafka 上获取数据来更新缓存。你可以将 Kafka 的主题设置为压缩型日志,应用程序每次在重启时就可以从零偏移量位置重新刷新缓存。
  3. 你的流式作业数据流来自 Kafka,在流式作业的逻辑发生变更后,需要重新计算结果。最简单的办法就是将偏移量重置为零,让新代码重新计算结果。
  4. Kafka 经常被用于捕获和分发数据库的变更事件(通常被称为 CDC,Change Data Capture)。应用程序可能只需要最新的数据库变更,但却要处理完整的数据快照,而这是相当耗时的操作。如果启用主题的日志压缩功能,就可以让应用程序直接从零偏移量位置重新加载数据。

像这样在 Kafka 里存储数据并不是什么疯狂事,Kafka 本来就是设计用来存储数据的。数据经过校验后被持久化在磁盘上,并通过复制副本提升容错能力。再多的数据都不会拖慢 Kafka,在生产环境中,有些 Kafka 集群甚至已经保存超过 1 TB 的数据。

那么人们为什么会对使用 Kafka 来存储数据心存疑问呢?

我想,人们更多的是把 Kafka 当成了消息队列系统。消息队列有一些不成文的规则,比如“不要在消息队列里保存消息”。传统的消息系统之所以不能用来保存消息,是因为:

  • 消息被读取后就会被删除
  • 伸缩性差
  • 缺乏健壮的复制机制(如果 broker 崩溃,数据也就丢失了)

传统的消息系统在设计上存在很多不足。从根本上讲,任何一个异步消息系统都会保存消息,只是时间很短,有时候只有几秒钟,直到消息被消费为止。假设有一个服务向消息队列发送消息,并希望有一种机制可以保证其他服务能够收到这个消息,那么消息就需要被保存在某个地方,直到其他服务读取它。如果消息系统不擅长存储消息,也就谈不上给消息“排队”了。你可能觉得无所谓,因为你并不打算长时间地保留消息。但不管怎样,如果消息系统持续地处理负载,总会有一些未被消费的消息需要保存下来。一旦消息系统发生崩溃,如果没有有效的容错存储机制,数据就会丢失。消息存储是消息系统的基础,但人们总是忽略这一点。

实际上,Kafka 并非传统意义上的消息队列,它与 RabbitMQ 等消息系统并不一样。它更像是一个分布式的文件系统或数据库。Kafka 与传统消息系统之间有三个关键区别。

  • Kafka 持久化日志,这些日志可以被重复读取和无限期保留
  • Kafka 是一个分布式系统:它以集群的方式运行,可以灵活伸缩,在内部通过复制数据提升容错能力和高可用性
  • Kafka 支持实时的流式处理

以上三点足以将 Kafka 与传统的消息队列区别开,我们甚至可以把它看成是流式处理平台。

我们可以这样来看待消息系统、存储系统和 Kafka 之间的关系。消息系统传播的是“未来”的消息:你连接到 broker 上,并等待新消息的到来。存储系统保存的是过去写入的数据:你查询或读取的结果是基于过去所做的更新。而流式处理可以把这二者结合起来,既可以处理过去的数据,也可以处理未来的消息。这也就是为什么 Kafka 的核心就是一个持续的、基于时间排序的日志。它是一种结构化的“文件”,而且从逻辑上看,它没有终点,会一直持续下去。应用程序不需要区分已有的旧数据和即将生成的新数据,它们都存在于一条持续的流中。Kafka 提供了统一的协议和 API 来保存过去的数据和传播未来的消息,Kafka 因此成为一种非常好的流式处理平台。

日志就像是分布式文件系统中的一个文件,在这个系统里,日志被复制到多台机器上,被持久化到磁盘,并支持高吞吐的线性读取和写入。当然,日志也像是一个消息系统,支持高吞吐的并发写入和低延迟的多消费者。

从实现方面来看,日志非常适合用来作为数据存储。Kafka 本身就是使用复制日志作为存储,所以你也不例外!在 Kafka 内部,偏移量被保存在一个压缩主题上,Kafka Streams API 使用压缩主题来记录应用程序的处理状态。

当然,把 Kafka 作为存储系统来用并不会给你带来新的门槛。存储系统包揽了正确性、运行时间和数据完整性等方面的工作。如果一个系统成为数据的标准来源,人们就会对它的正确性和运维标准提出很高的要求。我们花了大量的精力在提升 Kafka 的正确性上,我们每天在数百台机器上运行数个小时的分布式测试以及数千个常规性的单元测试,但我们觉得还有很多事情要做。除了测试之外,我们还需要知道如何做好运维工作,以及了解系统的局限性。

有时候,人们也会问我,这是不是就意味着 Kafka 可以取代其他存储引擎。答案当然是否定的。

首先,数据库提供大量的查询,而 Kafka 并不打算在日志上增加随机访问的特性。Kafka 保存数据可以被复制到其他数据库、缓存、流式处理器、搜索引擎、图存储引擎和数据湖(data lake)上,这些存储引擎都各自的优缺点,我们也无法做出一个可以打败其他所有引擎的系统。

如果说 Kafka 并不想取代这些系统,那它存在的意义是什么?你可以把数据中心看成是一个大型的数据库,Kafka 是这个系统里的提交日志,而其他存储引擎则是索引或视图。Kafka 是构建数据库的基础,至于查询方面的工作可以交给索引和视图。

Kafka Streams API 提供了交互式的查询功能。基于 Kafka Streams 开发的应用就是一个 Kafka 消费者,只不过它们可以维护计算状态,而且这些状态可以直接保存到外部的存储系统,这种物化视图让 Kafka 具备了低延迟的查询能力。Kafka 集群保存日志,Streams API 保存物化视图并处理查询请求。后来我们引入了KSQL——Kafka 的流式 SQL 引擎。有了 KSQL,用户可以直接使用 SQL 语句从 Kafka 上获得物化视图。

我们不打算为 Kafka 提供查询 API 的另一个原因是因为我们有其他更重要的事情要做。我们希望流式处理成为主流的开发模式,让流式平台成为现代数字业务的中心系统。我们希望能够达成这个让人激动不已的目标,而不只是创建一种新的数据库系统。我们相信,在现代企业里,流式平台将会成为移动和处理数据的黑马。要实现这个目标,我们还有很多事情要做。


感谢郭蕾对本文的审校。

给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们。

语言 & 开发架构