Kafka真正定位并不是消息系统

本文是Kafka创始人的一篇博客,认为Kafka可以用于像数据库那样持久存储,这与人们通常对消息系统的印象不同,其实Kafka真正定位是一个日志系统,消息队列只是其一个应用模式,如同会气功的人玩劈砖一样,腾讯将Kafka改为真正消息系统用于微信也可见Kafka的内功深厚,其在大数据分析领域配合Kafka Stream将是Storm/Spark以后的流式计算新贵。

原文大意如下:


人们经常询问Apache Kafka的一个问题是:是否可以将其用于长期存储。其实Kafka本身就是一个存储记录的日志系统。

问题是您是否真的可以将这种日志看成文件系统一样呢?将其用作数据的真实存储库。

显然这是可能的,如果您将默认的保留(retention )设置为“永远(forever)”或启用主题的日志压缩功能,那么数据将一直保留在Kafka中。但我认为人们问这个问题真正意思是,这是否会奏效?是完全疯狂吗?

简单的答案是,这不是疯狂的,人们一直这样做,而卡夫卡实际上是为这种类型的使用而设计的。但首先,你需要了解为什么要这样做?这里有一些实际用例:

1.您可能正在使用事件溯源(Event Sourcing)构建应用程序,并需要一个存储变动的日志。理论上你可以使用任何系统来存储这种变动修改日志,但是Kafka直接解决了不可变的日志和“物化视图”中的很多问题。见:纽约时报将其作为所有的文章数据存储核心

2.您可能在应用程序的每个实例中都有一个内存缓存,由Kafka提供更新。一个非常简单的构建方法是将Kafka主题进行日志压缩,并且只要重新开始导入数据到缓存中时,应用程序只要从零偏移的开始位置重新开始刷新就行了。

3.通过卡夫卡实现流处理进行数据流计算时,当流处理代码的逻辑发生变化时,您经常要重新计算结果。一个非常简单的方法是将程序的偏移量重置为零,以使用新的代码重新计算结果。有时候,这个有趣的名字叫做“Kappa架构”。

4.Kafka通常用于捕获和分发数据库的更新流(这通常称为Change Data Capture更改数据捕获,简称CDC)。正常情况下已经在运行的应用程序通常只需要数据的最新改动,但是新的应用程序则需要从头开始使用整块数据。然而,执行大型生产数据库的完全转换存储通常是非常精细和耗时的操作。在主题上启用日志压缩可以通过重置为零来简化这种数据的重新加载操作。

那么这样做是疯狂的吗?答案是否定的,在Kafka中存储数据并不奇怪,因为它就是为了这样做而设计的。Kafka中的数据被持久化到磁盘,校验和复制以进行容错。累积更多的存储数据并不会减慢。有Kafka集群在生产中运行超过一百亿字节存储数据的案例。

那么为什么在Kafka中存储数据这件事会让人们非常关心,因为这显然是像存储系统应该做的事情?

我认为,人们关注卡夫卡通常是因为它被描述为消息队列系统。使用消息队列的前两个规则是:“您不可以在消息队列中存储消息”。这在传统消息传递系统中是有以下原因:

1. 因为读消息时会删除它
2. 因为消息系统的数据量不足,数据累积起来会出现超出内存的情况。
3. 因为消息传递系统通常缺乏强大的复制功能(因此,如果消息系统程序死机,您的数据也可能会消失)。

这些传统消息系统的设计实际上是有很大的缺陷。毕竟,如果你考虑到,任何异步消息传播基本底层都会存储消息,即使它们只是在几秒钟之内就被消费。我的服务发送一个消息到队列中后就可以继续做其他业务,但我想要一些保证机制确保其他服务能最终会收到和处理该消息,因此必须在某处存储它,直到其他服务消费后删除它。所以如果你的消息系统不能很好地存储消息,那么它也不是“队列”消息系统。您可能会认为这并不重要,因为您不打算长时间存储消息。但是无论消息系统中的消息如何简短,如果消息系统处于连续的高负载状态,总是会存储一些未被消息的消息。所以当这个系统失败出错时,如果它没有提供容错存储的能力,那就会丢失数据。因此,做消息系统的功能就需要拥有存储能力,这一点似乎很明显,但是当人们评估消息传递系统时,经常会错过。

因此,存储是消息系统使用中非常重要的判断标准。但实际上,卡夫卡并不真正是传统意义上的消息队列,在实现中,它看起来不像RabbitMQ或其他类似的技术。在架构上相比传统的消息队列,它更接近于分布式文件系统或数据库。卡夫卡与传统信息系统有三个主要区别:

1. 正如我们所描述的那样,Kafka是一个永久性存储日志系统,可以重头读取数据并无限期保存数据。

2.卡夫卡是作为现代分布式系统构建的:它可以集群方式运行,可以弹性扩展或伸缩,并在内部复制数据,以实现容错和高可用性。

3.Kafka可以进行实时流处理,而不仅仅是一次处理单个消息。这允许在更高级别的抽象中处理数据流。

我们认为这些差异足以证明,将卡夫卡视为消息队列非常不准确,而应将其分类为流式平台。

消息系统、存储系统和Kafka之间的关系如下。

消息系统都是传播未来消息,当您正在等待新消息到达时,您等待的是在未来某个时刻将到来的消息。

存储系统(如文件系统或数据库)都是关于存储过去的写入数据的:当您查询或从中读取数据时,您将根据过去所做的更新获取结果。

流处理的本质是能够将这两个二者结合起来,可以处理过去,并在未来有新消息时可继续处理。这就是为什么卡夫卡的核心抽象是一个连续的时间上有序的日志。

这个抽象的关键在于,它是一种结构化的“文件”,当您到达文件最后一个字节时并不会结束,而是在逻辑上至少是永远。因此,写入日志的程序不需要区分已经发生的数据和将来会发生的数据,这一切都表现为一种连续的流。

存储过去和将未来传播到单一统一协议和API之后的这种组合正是Kafka在流处理方面发挥作用的关键点。

这个存储的日志非常类似于分布式文件系统中的文件,因为它跨机器复制,持久化到磁盘,并支持高吞吐量的线性读写,但它也像一个消息系统,它允许许多许多高吞吐量并发写入,并且对消息的发布时间进行了非常精确的定义,以允许对许多消费者进行进行低成本的、低延迟的变动传播。在这个意义上,它是两个世界中最好的。

在实施中,这个复制的日志非常适合用作存储,这不是意外设计的功能。事实上,卡夫卡本身就是存储,所以你不用回避!内部卡夫卡存储并跟踪消费者使用相应主题中的位置偏移量,Kafka的Streams API使用压缩主题作为应用程序处理状态的日志。这两种用例都需要永久存储所写入的数据。

存储系统在正确性、正常运行时间和数据完整性方面真正承担了巨大的负担。我参与了在LinkedIn建立和运行几代分布式数据库,当一个系统被视为数据的规范来源时,软件正确性和操作实践的标准大大增加。我们为Kafka的正确性付出了很大的努力 - 除了成千上万的正常单元测试之外,我们每天都会运行数百次机器时间的分发性折磨测试,但是老实说总是会做更多的事情。但是,除了测试之外,如果您正在为这种用例运行Kafka,那么您需要确保知道如何良好运行,并且需要知道系统的限制。这是Confluent可以帮助的:我们提供支持和工具,用于自己管理和监控Kafka,或托管服务。

当我和其他人谈论这个问题时,他们有时候会问,如果这意味着卡夫卡可以成为一种通用的数据库,那么就会废除所有其他的存储空间(显然我和很多卡夫卡粉丝交谈)。答案可能不是,有两个原因:

首先,数据库主要是关于查询,我不认为Kafka真的会尝试直接针对日志添加任何类型的随机访问查找。相反,它正在做的是将可复制的数据的日志存储到任意数量真正的数据库、缓存、流处理器、搜索引擎、图形存储和数据湖中(更不用说定制应用程序或SaaS产品)。这些系统中的每一个都有自己的利弊,我认为在一个单一的系统中,你可以比所有这些系统都更好。这些存储通常需要与其自己的存储布局非常紧密(例如,分析数据库具有非常复杂的柱状索引技术,搜索索引保持反向索引,缓存在内存中,LSM有利于写入,btrees优于读取等) 。

如果卡夫卡不会成为查询的通用格式,那它是什么呢?我认为将你的数据中心视为一个巨大的数据库,在该数据库中,Kafka是确认数据库提交SQL操作的日志,各种存储系统是派生的索引或各种视图。

这是真的,像Kafka这样的日志可以看成是建立数据库的原始数据,但是查询仍然由根据恰当访问模式而构建的索引提供。

其中最好的例子之一是Kafka Streams API中的交互式查询功能。Kafka Streams应用程序只是另一个Kafka消费者,但他们可以保持状态的持续计算,这是一种流。这种状态可以直接查询或者输出到外部系统中。不包括直接查询Kafka,而是使流程处理应用程序能够在Kafka中保留数据的派生的、可查询的物化视图,并对其执行低延迟查询。

Kafka集群用来存储日志,流处理API用来存储物化视图并对其进行查询。您可以将其视为应用程序和数据库之间分界线的一种重构。当我们添加KSQL时,这变得更有趣,它是一个用于卡夫卡的流式SQL引擎。使用KSQL,您不再需要编写任何代码,但可以使用SQL语句来连续地转换和计算Kafka中的物化视图(并且很快就对这些视图进行查询)。

但是也许Kafka不添加查询API的另一个原因是它有一个更令人兴奋的使命。卡夫卡的目标是使数据流和流处理成为主流开发范例,并使这种类型的流媒体平台成为现代数字业务的中枢神经系统。

我认为这实际上比建立第1001个数据库更有趣。我认为这种类型的流媒体平台对于数据在现代企业中的移动和处理以及现代实时应用程序的构建方式至关重要。所以我们把其作为重点放在第一位。