0
点赞
收藏
分享

微信扫一扫

「企业事件枢纽」Apache Kafka支持ACID事务吗?

「企业事件枢纽」Apache Kafka支持ACID事务吗?_数据库

我花了很多时间来解释消息队列和事件流系统之间的区别。消息队列系统(如IBM MQ)和事件流系统(如Apache Kafka)之间的最大区别在于流历史的概念。本质上,在事件流系统中,事件流中的历史事件在被使用时不会立即删除。他们呆在。

还有一个主要的区别,那就是交易。事务基本上是一种跨资源保持一致性的方法。在事务系统中,保持事物一致性的硬逻辑是基础设施的一部分,而不是应用程序的一部分。应用程序在事务的范围内执行其工作,然后提交事务,在知道事务的所有影响或没有影响的情况下安全地提交事务。

正如MQ中的主题与Kafka中的主题不太一样,MQ中的事务也与Kafka中的事务不太一样。

Kafka确实有一个称为一次性语义的特性,但是它提供的保证比正确的事务弱得多。它看起来像是API级别的事务,但是如果你仔细观察,就会发现它是不一样的。在很多情况下,Kafka保证是足够的,但是如果您习惯了正确的ACID事务(稍后我将解释这一点),我将花时间来理解它们之间的区别。

消息传递和事务的实践

让我们看一些例子。最基本的例子是这样的:

  1. 开始事务
  2. 使用主题T1中的消息
  3. 向主题T2生成消息
  4. 提交事务

它只是将消息从主题T1移动到主题T2。在事务期间,消息传递操作的效果不是永久性的,但是当它提交时,它们都变成永久性的。如果事务失败,操作都将撤消。

一个更复杂的示例涉及两个不同的资源管理器,我将使用消息传递系统和关系数据库进行演示。消息传递系统用于将数据安全地从一个数据库转移到另一个数据库。

第一个涉及源数据库和消息传递系统的事务如下:

  1. 开始事务
  2. 从源数据库中读取行
  3. 生成包含主题T的行数据的消息
  4. 从源数据库中删除行
  5. 提交事务

然后,涉及目标数据库和消息传递系统的第二个事务如下:

  1. 开始事务
  2. 使用包含主题T的行数据的消息
  3. 将行插入目标数据库
  4. 提交事务

对于两个事务之间的时间段,数据库中的数据实际上只在消息传递系统中。数据库中的行与消息之间存在精确的一对一关系。这里的关键是,在这两个事务中,数据库和消息传递系统被协调,以便它们一起提交。这是一个分布式事务的例子,它使用了一种称为两阶段提交的技术。

在这一点上,完全有理由提出这样的问题:为什么有人要构建一个基于分布式事务和两阶段提交的系统?当然,这是一种反模式。这是不成比例的,对吧?嗯,可能如此,但是存在许多广泛使用MQ和数据库事务的业务应用程序,因为应用程序逻辑非常简单。常规的应用程序团队可以实现在系统之间移动数据的神奇壮举,可能跨越很大的距离,而不会丢失或重复。

IBM MQ可以轻松实现这两个示例。Apache Kafka只能轻松地完成第一个任务。如果您是一个完全的专家,那么您也可以使用一些非常仔细编写的应用程序代码来实现第二个目标,以确保在所有情况和故障模式下都没有数据丢失和重复。这一点也不简单,我也见过有人尝试过,但都失败了。

所以,我的观点是,用Kafka在技术上是可行的,但它增加了应用程序的复杂性。

消息传递和ACID属性

事务系统实现四个ACID属性:原子性、一致性、隔离性和持久性。这些概念的定义确实适用于数据库,但总体思想也适用于消息传递系统。大概是这样的:

  1. 事务表现为单个原子单元,它要么完全成功,要么完全失败
  2. 事务的所有影响都同时对所有观察者可见
  3. 事务一旦提交,即使在系统出现故障的情况下,它仍然会提交

在IBM MQ中,每个队列管理器都有一个恢复日志,所有消息传递操作和事务的日志记录都被附加到其中。日志以同步方式写入磁盘的临界点相对较慢,但在数据完整性方面有好处。一旦表示事务提交的日志记录被写入日志,您就知道事务是正确的原子性和持久性的。

在Apache Kafka中,精确的一次语义api是流处理应用程序的强大工具,但是事务保证相对较弱。如果一个事务使用两个不同的分区,每个分区的负责人负责将操作记录到自己的日志中。还有一个内部主题用于记录整个事务状态。因此,事务的持久状态分布在多个日志和可能的多个服务器上。如果您研究Kafka中事务提交的设计,它看起来有点像两阶段提交,在事务状态主题上有准备提交的控制消息,然后在实际主题上有提交标记,最后在事务状态主题上有提交控制消息。它很聪明,但更脆弱。然后考虑Kafka异步写入日志的方式,您会发现Kafka认为提交的事务根本不是原子事务。

在正常的操作下,它会工作得很好,但是不需要太多的想象力就可以想到一个失败,可以打破酸。例如,如果一个分区复制不足,并且leader遇到意外的断电,可能会发生少量数据丢失,从而破坏事务的完整性。一个相关的硬故障,比如影响所有代理的停电,甚至可能导致提交/中止标记在所有副本中丢失。您以这样一种方式部署Kafka,以最小化并希望消除这类问题,但是混合中仍然有异步持久性的元素。

如果存在与消息传递系统协调的数据库等其他资源,那么这一点尤其重要。我们需要两个系统的事务保证级别来匹配。一致性和持久性保证必须平等地适用于所有资源。如果事务中的一个参与者在失败后有点健忘,事务完整性就会丢失。这就是为什么在与其他资源管理器协调时同步写入日志是如此重要的原因;它明确提供了什么级别的保证,这使得在所有系统上进行匹配变得很容易。

结束

现在您确切地理解了ACID事务和Kafka之间的区别—一次语义(exactly-once-semantics)。如果您不仔细查看现有的代码,不考虑不同部分需要什么基本保证,不非常仔细地设计其替换,那么您就不能选择一个使用事务的业务应用程序,然后使用Apache Kafka获得完全相同的结果。您当然可以构建合适的、坚如磐石的业务应用程序,但是您编写的代码可能非常不同。例如,您可能选择允许偶尔的消息复制,以便能够安全地重试,这可能会带来对消息的幂等处理。不完全是rock-solid科学,但不同。对于使用Kafka Streams API的流处理应用程序来说,exactly-once semantics 处于最佳状态,就非常有意义了。

那么,Apache Kafka做ACID事务吗?绝对不是。不可能。你能得到类似的效果吗?如果你以正确的方式设计你的应用程序,是的。这有关系吗?在很多情况下,并不是这样,但当它出现时,你绝对不想出错。只要花点时间去理解您需要的保证,使您的系统可靠,并做出相应的选择。

举报

相关推荐

0 条评论