XA事务中的数据一致性

23
假设我们有一个数据库(如Oracle)和一个JMS提供者(如HornetQ),它们参与一个XA事务。消息被发送到JMS队列,并且一些数据在同一分布式事务中持久化到数据库中。事务提交后,消息消费者将读取已持久化的数据并在单独的事务中处理它们。
关于第一个XA事务,在执行时,事务管理器(如JBoss)可能执行以下事件序列:
  1. 准备 (HornetQ)
  2. 准备 (Oracle)
  3. 提交 (HornetQ)
  4. 提交 (Oracle)
如果消息消费者在HornetQ中完成提交后开始读取数据,但在Oracle中仍在执行,则会发生什么?消息消费者会读取过期的数据吗?
这个问题可以推广到任何种类的参与XA事务的多个资源,即在提交阶段执行时是否存在小的时间窗口,在此窗口内来自另一个并发事务的读取器可能会获得不一致状态(通过从一个资源读取已提交数据和从另一个资源读取过期数据)?
我认为,事务性资源避免这种情况的唯一方法是在准备阶段完成后封锁所有受影响数据的读取器,直到提交被发出。这样,上述例子中提到的消息消费者将被阻塞,直到数据在数据库中提交。

好问题,这是我个人认为JTA的主要问题,它没有得到适当的文档支持,即使规范也远远不足以描述(正如应该)这样一个复杂的机制。 - Nicolas Filotto
此外,这还不是最糟糕的情况,想想当XAResource实现者没有覆盖恢复时,在提交时发生故障的情况。 - Nicolas Filotto
1
详细规范太长,无法在答案中列出,但可以在Oracle白皮书"XA和Oracle控制的分布式事务"第12页的“分布式事务和数据库锁定”章节中找到。 - ThinkJet
我设置了完全相同类型的多资源事务(2个数据源,一个JMS队列),我从未注意到这样的行为。从transactionManager的角度来看,HornetQ和数据库实例都是远程的,这并不意味着不可能,但在我看来非常不可能(也许使用JMS传输的inVM netty接收器和巨大的DB访问延迟)。 - Gab
4个回答

7

很遗憾,XA事务不支持一致性。当映射到CAP定理时,XA解决了多个数据存储中的可用性和分区容错性。在这样做时,它必须牺牲一致性。使用XA时,您必须接受最终一致性。

无论如何,创建CP或AP系统已经足够困难,无论您使用什么数据存储或事务模型,都会面临这个问题。


1
我有一些基于Weblogic JMS和Oracle 11g的不同环境经验。在这篇答案中,我假设它正好工作。希望我的回答能帮到你。
在我们的情况下,有一个“远程”系统,根据本地系统内发生的不同事件,必须通知该系统。另一个系统也读取了我们的数据库,因此用例似乎与您的问题几乎相同。事件的顺序与您的完全相同。在测试系统上没有出现任何故障。每个人都认为它会工作,但我们中的一些人怀疑它是否是正确的解决方案。当软件投入生产后,一些BPM流程运行得不可预测。所以对于你的问题,简单的答案是:是可能的,并且每个人都应该意识到这一点。
我们的解决方案(在我看来)并不是一个精心计划的方案,但我们意识到两次提交之间的时间窗口破坏了系统,所以我们向队列中添加了一些“延迟”(如果我记得没错应该是1-2分钟)。这足以完成其他提交并读取一致的数据。在我看来,这不是最好的解决方案。它没有解决同步问题(如果Oracle事务超过1-2分钟会怎样?)。 这里有一篇很棒的博客文章值得一读,我认为最后的解决方案是最好的。我们在另一个系统中实施了它,效果要好得多。需要注意的重要事项是应该限制重试(重新读取)以防止“卡住”的线程。(带有一些错误报告。)在这些限制下,我目前找不到更好的解决方案,所以如果有人有更好的选择,我期待着听到它。 :)
编辑:错别字。

如果Oracle事务超过1-2分钟怎么办?我们所说的延迟是提交事务的时间,而不是整个事务持续时间,最多只有几十毫秒。此外,JMS消息通常通过网络发送,这应该比第二阶段提交需要更长的时间。根据我的看法,您的设计还存在另一个问题。 - Gab
这个问题是否可以通过在消息消费者上获取数据锁(例如select for update)来更轻松地解决,而不是使用超时呢?使用超时,要么我们会为每个消息引入太大的固定延迟,要么如果超时时间太短,则增加了再次读取过期数据的风险。 - Dragan Bozanovic
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Hash
如果更改是插入语句,则返回True。但如果是更新,则应该使用锁定。 - Dragan Bozanovic
这真的取决于情况。一些基于时间戳的锁定策略会中止原始事务,而不是将第二个事务放置在“等待锁定”状态。我真的很想知道你的最终解决方案是什么。请在您有机会重现它时更新我们。 :) - Hash

0
我将插入一个状态字段,因此在每个步骤之后,如果成功,状态将被更新,读者在执行操作之前应检查状态。

0

是的。即使事务失败并回滚,外部系统仍然可以在DB实际提交之前接收和消费您发送的消息。

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

有时,某个消息对于JMS来说太大而导致send()失败,并且整个事务回滚(),但外部消费者仍然会在回滚之前接收和处理每个单独的消息。我知道这一点,因为他们会为每个处理的消息向我发送电子邮件,而我收到了许多关于未在DB中标记为已发送的消息的电子邮件(因为事务已回滚)。

如果该外部系统以某种方式SELECT COUNT(*)来计算我的系统发送的消息数量,则它将读取0条已发送的消息,尽管它已经消费了数百条消息。

是的,即使使用XA事务,外部系统仍然有可能读取过期数据。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接