我有一个应用程序,我需要将一些数据存储在数据库(例如mysql)中,然后在消息队列中发布一些数据。我的问题是:如果应用程序在数据库中存储后崩溃,我的数据将永远不会写入消息队列然后丢失(因此我的系统的最终一致性将无法保证)。 我该如何解决这个问题?
答案 0 :(得分:13)
我有一个应用程序,我需要将一些数据存储在数据库(例如mysql)中,然后在消息队列中发布一些数据。我的问题是:如果应用程序在数据库中存储后崩溃,我的数据将永远不会写入消息队列然后丢失(因此我的系统的最终一致性将无法保证)。我该如何解决这个问题?
在这种特殊情况下,答案是从数据库加载队列数据。
也就是说,在用于写入数据的同一事务中,将需要排队的消息写入数据库。然后,异步地,您从数据库中读取该数据,并将其写入队列。
见Udi Dahan的Reliable Messaging without Distributed Transactions。
如果应用程序崩溃,恢复很简单 - 在重新启动期间,您在数据库中查询所有未确认的消息,然后再次发送。
请注意,此设计确实希望消息的使用者设计为at least once delivery。
答案 1 :(得分:3)
评论太长了。
我假设你有一个无损消息队列,一旦你得到写入数据的确认,队列就保证有记录。
基本上,您需要一个循环,其中包含可以回滚的事务或数据库中的状态。事务的伪代码是:
就个人而言,我可能会以以下状态执行此操作:
如果从故障中恢复,您可能需要检查消息队列以查看是否有任何“待处理”记录实际写入队列。
答案 2 :(得分:1)
除了@Gordon Linoff所说的,假设持久消息传递(类似于MSMQ?),方法/处理程序将是事务性的,所以如果它全部成功,则消息将被写入队列并且数据到您的视图模型,如果失败,一切都将失败......
要缓解ID问题,您需要使用GUID而不是DB生成的密钥(如果您使用的是消息,则无论如何都需要删除参照完整性并将GUIDS作为密钥引入)。
还有一个建议,不要更新数据库,但是只插入/ upsert(挂起的行然后是完成的行)并让读者根据最新的行(例如)进行数据的投影
答案 3 :(得分:1)
恐怕答案(VoiceOfUnreason,乌迪·达汉)只会把问题笼罩在地毯下。地毯下的问题是:应该如何设计从数据库到队列的数据移动,以使消息仅发布一次(不使用XA)。如果解决了这个问题,那么您可以通过任何其他业务逻辑轻松扩展该概念。
CAP theorem清楚地告诉您限制。
XA交易不是100%的防弹解决方案,但在我看来,我认为是最好的选择。
答案 4 :(得分:0)
将消息作为事务的一部分是一个好主意,但是它有很多缺点,例如
如果您的
a。数据库/语言不支持事务
b。交易是耗时的操作
c。您无法在响应服务呼叫时等待队列响应。
d。如果您的数据库已经处于压力之下,那么编写消息将加剧更高工作负载的影响。最佳做法是使用数据库流strong>。大多数现代数据库都支持流(Dynamodb,mongodb,orcale等)。您有运行数据库流的使用者,该数据库流从数据库流中读取数据并写入队列或使高速缓存无效,将其添加到搜索索引器等。一旦所有这些操作成功,就将流项目标记为已处理。
这种方法的优点
在区域故障的多区域部署中可以使用。 (您应该阅读区域数据流并充实所有区域数据存储。)
没有写更多记录或性能瓶颈的麻烦。
您可以将此模式用于其他数据源,例如缓存,排队,搜索。
缺点
您可能需要调用多个服务来构造适当的消息。
一个数据库流可能不足以构造适当的消息。
确保流的可靠性,例如redis stream is not reliable
注意,这种方法也不能保证语义一模一样。使用者逻辑应该是幂等的,并且应该能够处理重复的消息