【发布时间】:2019-09-23 16:54:57
【问题描述】:
我有两个应用程序exporter 和payment_handler
-
exporter执行SELECT * FROM orders WHERE status="PAID",处理行(大约 5 分钟),最后执行UPDATE orders SET status="EXPORTED" WHERE status="PAID" -
payment_handler执行UPDATE orders SET status="PAID" WHERE status="UNPAID"
如果没有事务隔离,这将导致在payment_handler 运行而exporter 处理行时更新错误的数据:导出器的UPDATE 语句会将SELECT 语句没有的订单标记为导出之前选择。
如果我将导出器包装在事务中并使用FOR UPDATE 限定符执行SELECT,则payment handler 将被阻止直到exporter 完成,因为MySQL 不允许status="PAID" 的新插入。但是付款处理程序不能被阻塞这么长时间。
我当前的解决方法是避免事务并在数据中创建我自己的“快照”:exporter 创建一个临时表export_ids 并执行如下插入:INSERT INTO export_ids SELECT id FROM order WHERE status="PAID"。 exporter 中 order 表的所有后续 SELECT 和 UPDATE 操作将加入/子选择 export_id 表。这在性能和存储方面都很好,因为一次导出在export_ids 中只有大约 100k 个条目。
但我想知道我使用 MySQL 的事务级别是否错误,是否有更好的方法来实现该行为?
【问题讨论】:
-
也许你应该分享一些代码,以便我们更好地理解/了解你在做什么,因为代码告诉了超过“1000”个单词..
-
也许我遗漏了一些东西 - 但是您在哪里看到可能的“竞争条件”? exporter 只选择带有
status="PAID"的行。 payment_handler 不会触及任何这些行。或者你真的在没有 WHERE 条件的情况下运行UPDATE orders SET status="EXPORTED"? -
@PaulSpiegel 我已将问题更新为更准确。 payment_handler 将状态更新为 exporter 执行 SELECT 和 UPDATE 的确切值。
-
在我看来,您这样做是正确的。我不知道另一种避免阻塞的方法。当然,我假设您已尽一切努力加快导出程序的进程,但速度仍然很慢。
-
您可以在github.com/gbirke/transaction-test查看代码示例
标签: mysql transactions transaction-isolation