使用MySql的消费者 - 生产者模式

时间:2016-01-02 00:36:20

标签: mysql database producer-consumer

大家好,祝大家新年快乐! :) 我正在开发一个软件(Java,后端),它有3个组件,每个组件都是独立的Java应用程序(你甚至可以将它们视为单独的线程): 1)DB数据导入器1。 2)DB数据导入器2。 3)DB数据导出器。

现在,所有这些应用程序都在使用相同的MySql数据库,使用相同的2个InnoDB表 - “Orders”和“Items”。每个订单可能包含0个或多个项目。所有数据库操作(数据查询和数据插入)都是使用存储过程完成的。这就是我的应用程序所做的:

应用程序1或2(记住,它们是独立的)每隔几秒开始导入“一个订单”。 “订单”被导入“订单”表,每个“项目”被导入“项目”表。 a)应用程序接受并订购,将该订单导入“订单”表并将订单标记为“未准备好导出”。 b)然后,应用程序继续导入该订单的所有项目(如果有)。 c)最后,当所有项目都被导入时,订单被标记为“准备出口”(我有一个专用的列)。

应用程序“3”每隔几秒执行一次: a)检查“准备出口”订单。 b)选择50“准备出口”订单并将其标记为“出口”。 c)将订单及其物品出口到文件中。 d)将所有“在出口中”的订单标记为“已导出”。

现在,正如您所知,即使使用专用列来说明哪个订单或项目应该是导出器,哪个仍在导入且尚未导出,我会遇到一些死锁和竞争条件。

您是否知道我可以使用任何简单而安全的机制来实现这个“生产者 - 消费者”系统,而无需锁定整个表“订单”和“项目”(因为它们被软件的其他部分主动使用)? 我猜使用3状态列并不是一个好主意,考虑到每个表可能有数百万行,而这样的3状态列上的索引是无效的。必须有更好的东西:)

1 个答案:

答案 0 :(得分:1)

您的处理工作流程包括以下步骤:

  

应用程序“3”每隔几秒执行一次:a)检查“准备好了   导出“订单.b)选择50”准备出口“订单并标记它们   作为“出口”。 c)将订单及其物品出口到文件中。 d)   将所有“在导出中”的订单标记为“已导出”。

重要的是如何执行此步骤。

如果您执行类似此过程的操作,则应该干净地运行。

在我看来,50只是一次批量太大了。我先从一个接一个地做,然后尝试使批量更大。

首先,在单个查询中将某些订单标记为“导出”这样您就不必担心交易。

UPDATE orders 
   SET status = 'exporting'
 WHERE status = 'ready-for-export'
 ORDER BY id
 LIMIT 50  

然后,在循环中执行此操作以处理匹配中的所有订单

 /* get an order to process */
 SELECT id, whatever, whatever 
   FROM orders
   WHERE status = 'exporting'
   ORDER BY id
   LIMIT 1

 /* if no order came back from this query, you are done with your batch */

 /* process the order */

 /* mark the order done */
 UPDATE orders SET status = 'exported' WHERE id = ???id??? AND status='exporting'

这会抓取一批订单(使用LIMIT 50)并将其标记为“准备出口”。然后它逐一咀嚼它们。

现在,为了避免订单表上的死锁,其余软件中的查询需要包含WHERE status <> 'exporting'或等效项,因此他们将忽略导出的行。

你可能会在Items表上遇到死锁。您可以通过始终执行像这样的单一查询操作来避免这些

 UPDATE items SET number_on_hand = number_on_hand - ???order_quantity???

您也可以查看MySQL的UPSERT版本。它被称为INSERT ... ON DUPLICATE KEY UPDATE.

但是,最重要的是:如果你有一个繁忙的系统,你将不得不使用交易。避免死锁的经典方法是始终以相同的顺序锁定资源。例如,

  1. 始终先锁定Order表行,或先锁定Item表行,但不要以相反的顺序。

  2. 如果必须在一个表中锁定多行,请始终以相同的顺序锁定它们 - 也就是说,在ORDER BY id查询中使用SELECT ... FOR UPDATE子句。

  3. 请注意,(status,id)上订单表上的索引将有助于优化查询以将订单分批并从批次中释放。