与数据库和消息队列记录的最终一致性
Eventual consistency with both database and message queue records
我有一个应用程序,我需要在其中将一些数据存储在数据库中(例如 mysql),然后在消息队列中发布一些数据。我的问题是:如果应用程序在存储到数据库后崩溃了,我的数据永远不会写入消息队列然后丢失(因此我的系统的最终一致性将无法保证)。
我该如何解决这个问题?
我假设你有一个loss-less消息队列,一旦你得到写入数据的确认,队列就保证有记录。
基本上,您需要一个循环,其中包含可以回滚的事务或数据库中的状态。一笔交易的伪代码是:
- 开始交易
- 插入数据库
- 写入消息队列
- 消息队列确认后,提交事务
就我个人而言,我可能会使用状态来执行此操作:
- 以“待定”状态(或类似状态)插入数据库
- 写入消息队列
- 消息确认后,将状态更改为“已提交”(或类似内容)
在从失败中恢复的情况下,您可能需要检查消息队列以查看是否有任何“待定”记录实际写入了队列。
加上@Gordon Linoff 所说的内容,假设持久消息传递(类似于 MSMQ?)method/handler 将是事务性的,因此如果全部成功,消息将被写入队列和数据到您的视图模型,如果失败,则全部失败...
为了缓解 ID 问题,您需要使用 GUID 而不是数据库生成的密钥(如果您使用的是消息传递,则无论如何都需要删除参照完整性并将 GUID 作为密钥引入)。
还有一个建议,不要更新数据库,而是插入only/upsert(待定行,然后是完成行)并让reader根据最新行(例如)
I have an application where I need to store some data in a database (mysql for instance) and then publish some data in a message queue. My problem is: If the application crashes after the storage in the database, my data will never be written in the message queue and then be lost (thus eventual consistency of my system will not be guaranted). How can I solve this problem ?
在这种特殊情况下,答案是从数据库加载队列数据。
也就是说,您将需要排队的消息写入 数据库 ,在用于写入数据的同一事务中。然后,异步地从数据库中读取该数据,并将其写入队列。
参见 Reliable Messaging without Distributed Transactions,作者 Udi Dahan。
如果应用程序崩溃,恢复很简单——在重新启动期间,您查询数据库以获取所有未确认的消息,然后再次发送它们。
请注意,此设计确实希望消息的消费者为 at least once delivery 设计。
恐怕答案(VoiceOfUnreason,Udi Dahan)只是把问题扫到地毯下。地毯下的问题是:应该如何设计数据从数据库到队列的移动,以便消息只发布一次(没有 XA)。如果您解决了这个问题,那么您可以通过任何额外的业务逻辑轻松地扩展该概念。
CAP theorem 告诉你界限清楚。
XA 事务不是 100% 防弹解决方案,但在我看来是我所见过的所有其他解决方案中最好的。
将消息作为事务的一部分写入是个好主意,但它有多个缺点,例如
如果你的
一个。 database/language不支持交易
b。交易是时间操作
c。您无法在响应服务呼叫时等待队列响应。
d。如果您的数据库已经承受压力,写入消息将加剧更高工作负载的影响。
最佳实践是使用数据库流。大多数现代数据库都支持流(Dynamodb, mongodb, orcale 等)。您有数据库流的使用者 运行,它从数据库流中读取并写入队列或使缓存无效,添加到搜索索引器等。一旦所有这些都成功,您将流项目标记为已处理。
这种方法的优点
在出现区域故障的多区域部署的情况下会起作用。 (您应该从区域流中读取数据并混合所有区域数据存储。)
没有写入更多记录的开销或队列的性能瓶颈。
您可以将此模式用于其他数据源以及缓存、排队、搜索。
缺点
您可能需要调用多个服务来构造适当的消息。
一个数据库流可能不足以构造适当的消息。
确保您的流媒体的可靠性,例如redis stream is not reliable
注意 这种方法也不能保证 exactly once 语义。消费者逻辑应该是幂等的,并且应该能够处理重复的消息
我有一个应用程序,我需要在其中将一些数据存储在数据库中(例如 mysql),然后在消息队列中发布一些数据。我的问题是:如果应用程序在存储到数据库后崩溃了,我的数据永远不会写入消息队列然后丢失(因此我的系统的最终一致性将无法保证)。 我该如何解决这个问题?
我假设你有一个loss-less消息队列,一旦你得到写入数据的确认,队列就保证有记录。
基本上,您需要一个循环,其中包含可以回滚的事务或数据库中的状态。一笔交易的伪代码是:
- 开始交易
- 插入数据库
- 写入消息队列
- 消息队列确认后,提交事务
就我个人而言,我可能会使用状态来执行此操作:
- 以“待定”状态(或类似状态)插入数据库
- 写入消息队列
- 消息确认后,将状态更改为“已提交”(或类似内容)
在从失败中恢复的情况下,您可能需要检查消息队列以查看是否有任何“待定”记录实际写入了队列。
加上@Gordon Linoff 所说的内容,假设持久消息传递(类似于 MSMQ?)method/handler 将是事务性的,因此如果全部成功,消息将被写入队列和数据到您的视图模型,如果失败,则全部失败...
为了缓解 ID 问题,您需要使用 GUID 而不是数据库生成的密钥(如果您使用的是消息传递,则无论如何都需要删除参照完整性并将 GUID 作为密钥引入)。
还有一个建议,不要更新数据库,而是插入only/upsert(待定行,然后是完成行)并让reader根据最新行(例如)
I have an application where I need to store some data in a database (mysql for instance) and then publish some data in a message queue. My problem is: If the application crashes after the storage in the database, my data will never be written in the message queue and then be lost (thus eventual consistency of my system will not be guaranted). How can I solve this problem ?
在这种特殊情况下,答案是从数据库加载队列数据。
也就是说,您将需要排队的消息写入 数据库 ,在用于写入数据的同一事务中。然后,异步地从数据库中读取该数据,并将其写入队列。
参见 Reliable Messaging without Distributed Transactions,作者 Udi Dahan。
如果应用程序崩溃,恢复很简单——在重新启动期间,您查询数据库以获取所有未确认的消息,然后再次发送它们。
请注意,此设计确实希望消息的消费者为 at least once delivery 设计。
恐怕答案(VoiceOfUnreason,Udi Dahan)只是把问题扫到地毯下。地毯下的问题是:应该如何设计数据从数据库到队列的移动,以便消息只发布一次(没有 XA)。如果您解决了这个问题,那么您可以通过任何额外的业务逻辑轻松地扩展该概念。
CAP theorem 告诉你界限清楚。
XA 事务不是 100% 防弹解决方案,但在我看来是我所见过的所有其他解决方案中最好的。
将消息作为事务的一部分写入是个好主意,但它有多个缺点,例如
如果你的
一个。 database/language不支持交易
b。交易是时间操作
c。您无法在响应服务呼叫时等待队列响应。
d。如果您的数据库已经承受压力,写入消息将加剧更高工作负载的影响。
最佳实践是使用数据库流。大多数现代数据库都支持流(Dynamodb, mongodb, orcale 等)。您有数据库流的使用者 运行,它从数据库流中读取并写入队列或使缓存无效,添加到搜索索引器等。一旦所有这些都成功,您将流项目标记为已处理。
这种方法的优点
在出现区域故障的多区域部署的情况下会起作用。 (您应该从区域流中读取数据并混合所有区域数据存储。)
没有写入更多记录的开销或队列的性能瓶颈。
您可以将此模式用于其他数据源以及缓存、排队、搜索。
缺点
您可能需要调用多个服务来构造适当的消息。
一个数据库流可能不足以构造适当的消息。
确保您的流媒体的可靠性,例如redis stream is not reliable
注意 这种方法也不能保证 exactly once 语义。消费者逻辑应该是幂等的,并且应该能够处理重复的消息