XA 事务中的数据一致性

Data consistency in XA transactions

假设我们有一个数据库(例如 Oracle)和一个 JMS 提供者(例如 HornetQ)参与 XA 事务。消息被发送到 JMS 队列,一些数据在同一个分布式事务中持久保存在数据库中。提交事务后,消息消费者将读取持久化数据并在单独的事务中处理它们。

对于第一个 XA 事务,事务管理器可能会执行以下事件序列(例如 JBoss)

  1. 准备 (HornetQ)
  2. 准备(甲骨文)
  3. 提交 (HornetQ)
  4. 提交(甲骨文)

如果消息消费者在HornetQ中提交完成后开始读取数据,但在Oracle中仍在执行,会发生什么情况?消息消费者会读取陈旧数据吗?

这个问题可以推广到参与 XA 事务的任何类型的多个资源,即是否有可能在短时间内 window(执行提交阶段时)其中 reader从另一个并发事务可以获得不一致的状态(通过从一个资源读取已提交的数据并从另一个资源读取陈旧数据)?

我想说,事务资源防止这种情况的唯一方法是在准备阶段完成后阻止所有 reader 受影响的数据,直到发出提交。这样,上面提到的示例消息消费者将阻塞,直到数据提交到数据库中。

我对基于 Weblogic JMS 和 Oracle 11g 的一些不同环境有一些经验。在这个答案中,我认为它的工作方式 完全相同 。希望我的回答对您有所帮助。

在我们的案例中,有一个 "distant" 系统,它必须根据本地系统内部发生的不同事件进行通知。另一个系统也进入了我们的数据库,因此用例似乎与您的问题几乎相同。事件的顺序与你的完全相同。在测试系统上没有一个故障。每个人都认为它会起作用,但我们中的一些人怀疑它是否是正确的解决方案。随着软件投入生产,一些 BPM 流程 运行 不可预测。所以对你的问题的简单回答:是的,这是可能的,每个人都应该意识到这一点。

我们的解决方案(在我看来)不是一个精心策划的解决方案,但我们认识到两次提交之间的短暂时间 window 正在制动系统,所以我们在队列中添加了一些 "delay"(如果我记得大概是 1-2 分钟)。完成其他提交并读取一致的数据就足够了。在我看来,这不是最好的解决方案。它没有解决同步问题(如果 oracle 事务超过 1-2 分钟怎么办?)。

Here 是一个很棒的博客 post,值得一读,最后一个解决方案对我来说似乎是最好的。我们在另一个系统中实施了它,并且效果更好。 重要的是要注意你应该限制重试(重新读取)以防止 "stuck" 线程。(有一些错误报告。)有了这个限制我找不到更好的到目前为止的解决方案,所以如果有人有更好的选择,我期待听到它。 :)

编辑:打字错误。

是的。外部系统有可能在 DB 实际提交之前 接收并使用您发送的消息,即使事务失败并在之后回滚也是如此。

在过去的两年里,我一直在使用 XA 事务维护和开发分布式系统,其中 WebSphere MQ 作为 JMS 提供程序,Oracle 11g 作为后备数据库。
它的批处理作业之一将从数据库中读取离线消息,将它们发送到 JMS 并将它们标记为已在数据库中发送——所有这些都是同一个 XA 事务的一部分。如果任何消息或数据库失败,事务将被回滚。

有时,一条消息对于 JMS 来说太大,导致 send() 失败,整个事务回滚 (),使数据库保持不变。
但是,外部消费者仍在接收和处理回滚前发送的每条消息。我知道是因为他们会为每条处理过的消息发送一封电子邮件给我,而且我收到了很多关于未在数据库中标记为已发送的消息的电子邮件(因为事务已回滚)。

如果这个外部系统以某种方式 SELECT 计算我的系统发送的消息数量,它会读取 0 条发送的消息,尽管它已经消耗了数百条消息。

所以,是的,即使在使用 XA 事务时,外部系统也有可能读取陈旧数据。

不幸的是,XA 事务不支持一致性。当映射到 CAP theorem 时,XA 解决了跨多个数据存储的可用性和分区容错性。这样做必须牺牲一致性。使用 XA 时,您必须接受最终一致性。

在任何情况下创建 CP 或 AP 系统都是 hard enough 无论您的数据存储或事务模型如何,您都会面临这个问题。

我将插入一个状态字段,所以在每个步骤之后,如果成功,状态将被更新,reader 应该在执行操作之前检查状态。