MySql 的消费者-生产者模式
consumer-producer pattern with MySql
大家好,新年快乐! :)
我正在开发一个软件(Java,后端),它有 3 个组件,每个组件都是独立的 Java 应用程序(您甚至可以将它们视为单独的线程):
1) 数据库数据导入器 1.
2) 数据库数据导入器 2.
3) 数据库数据导出器。
现在,所有这些应用程序都使用相同的 MySql 数据库,使用相同的 2 InnoDB tables - "Orders" 和 "Items"。每个订单可能有 0 个或多个项目。所有的数据库操作(数据查询和数据插入)都是使用存储过程完成的。这是我的应用程序的作用:
应用程序 1 或 2(请记住,它们是独立的)每隔几秒开始导入 "an order"。 "Order" 正在导入 "Orders" table,每个 "Item" 正在导入 "Items" table。
a) 应用程序获取订单,将该订单导入 "Orders" table 并将订单标记为 "not ready for export"。
b) 然后应用程序继续为该订单导入所有项目(如果有)。
c)最后,当所有项目都被导入时,订单被标记为"ready for export"(我有一个专门的列用于该位)。
应用程序“3”每隔几秒执行一次:
a) 检查 "ready for export" 个订单。
b) 选择 50 "ready for export" 个订单并将它们标记为 "in export"。
c) 将订单和他们的项目导出到一个文件中。
d) 标记所有 "in export" 到 "exported".
的订单
现在,如您所知,即使有专门的列告诉哪些订单或项目应该出口,哪些仍在进口但不应该出口,我还是遇到了一些僵局和竞争条件。
你知道我可以用什么简单安全的机制来实现这个 "producer - consumer" 系统而不锁定整个 tables "Orders" 和 "Items"(因为它们是主动的被软件的其他部分使用)?
我猜想使用三态列不是一个好主意,因为每个 table 可能有数百万行,并且在这种三态列上的索引是无效的。必须有更好的东西:)
您的处理工作流程包括以下步骤:
Application "3" every few seconds does this: a) checks for "ready for
export" orders. b) selects 50 "ready for export" orders and marks them
as "in export". c) exports orders and theirs items into a file. d)
marks all orders that are "in export" to "exported".
这一步很重要。
如果您执行类似此过程的操作,它应该 运行 干净利落。
在我看来,一次做 50 个太大了。我会先一个一个地做,然后试着把批次做大一点。
首先,在单个查询中将一些订单标记为 'in export' 。 这样您就不必担心交易。
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
)并标记它们 "ready-for-export." 然后它会一个一个地咀嚼它们。
现在,为了避免在订单 table 上出现死锁,软件其余部分中的查询需要包含 WHERE status <> 'exporting'
或等效项,因此它们将忽略正在导出的行。
您可能在项目 table 上遇到了死锁。你可以通过总是像这样single-query操作
来避免这些
UPDATE items SET number_on_hand = number_on_hand - ???order_quantity???
您也可以查看 MySQL 的 UPSERT 版本。它被称为 INSERT ... ON DUPLICATE KEY UPDATE.
但是,底线是:如果您的系统很忙,您将不得不使用事务。避免死锁的经典方法是始终以相同的顺序锁定资源。例如,
始终首先锁定订单 table 行,或首先锁定项目 table 行,但绝不能以相反的顺序。
如果您必须将几行锁定在一个 table 中,请始终以相同的顺序锁定它们——也就是说,在 [=17] 中使用 ORDER BY id
子句=]查询。
请注意,(status,id)
上订单 table 的索引将有助于优化查询以将订单放入批次中并从批次中释放它们。
大家好,新年快乐! :) 我正在开发一个软件(Java,后端),它有 3 个组件,每个组件都是独立的 Java 应用程序(您甚至可以将它们视为单独的线程): 1) 数据库数据导入器 1. 2) 数据库数据导入器 2. 3) 数据库数据导出器。
现在,所有这些应用程序都使用相同的 MySql 数据库,使用相同的 2 InnoDB tables - "Orders" 和 "Items"。每个订单可能有 0 个或多个项目。所有的数据库操作(数据查询和数据插入)都是使用存储过程完成的。这是我的应用程序的作用:
应用程序 1 或 2(请记住,它们是独立的)每隔几秒开始导入 "an order"。 "Order" 正在导入 "Orders" table,每个 "Item" 正在导入 "Items" table。 a) 应用程序获取订单,将该订单导入 "Orders" table 并将订单标记为 "not ready for export"。 b) 然后应用程序继续为该订单导入所有项目(如果有)。 c)最后,当所有项目都被导入时,订单被标记为"ready for export"(我有一个专门的列用于该位)。
应用程序“3”每隔几秒执行一次: a) 检查 "ready for export" 个订单。 b) 选择 50 "ready for export" 个订单并将它们标记为 "in export"。 c) 将订单和他们的项目导出到一个文件中。 d) 标记所有 "in export" 到 "exported".
的订单现在,如您所知,即使有专门的列告诉哪些订单或项目应该出口,哪些仍在进口但不应该出口,我还是遇到了一些僵局和竞争条件。
你知道我可以用什么简单安全的机制来实现这个 "producer - consumer" 系统而不锁定整个 tables "Orders" 和 "Items"(因为它们是主动的被软件的其他部分使用)? 我猜想使用三态列不是一个好主意,因为每个 table 可能有数百万行,并且在这种三态列上的索引是无效的。必须有更好的东西:)
您的处理工作流程包括以下步骤:
Application "3" every few seconds does this: a) checks for "ready for export" orders. b) selects 50 "ready for export" orders and marks them as "in export". c) exports orders and theirs items into a file. d) marks all orders that are "in export" to "exported".
这一步很重要。
如果您执行类似此过程的操作,它应该 运行 干净利落。
在我看来,一次做 50 个太大了。我会先一个一个地做,然后试着把批次做大一点。
首先,在单个查询中将一些订单标记为 'in export' 。 这样您就不必担心交易。
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
)并标记它们 "ready-for-export." 然后它会一个一个地咀嚼它们。
现在,为了避免在订单 table 上出现死锁,软件其余部分中的查询需要包含 WHERE status <> 'exporting'
或等效项,因此它们将忽略正在导出的行。
您可能在项目 table 上遇到了死锁。你可以通过总是像这样single-query操作
来避免这些 UPDATE items SET number_on_hand = number_on_hand - ???order_quantity???
您也可以查看 MySQL 的 UPSERT 版本。它被称为 INSERT ... ON DUPLICATE KEY UPDATE.
但是,底线是:如果您的系统很忙,您将不得不使用事务。避免死锁的经典方法是始终以相同的顺序锁定资源。例如,
始终首先锁定订单 table 行,或首先锁定项目 table 行,但绝不能以相反的顺序。
如果您必须将几行锁定在一个 table 中,请始终以相同的顺序锁定它们——也就是说,在 [=17] 中使用
ORDER BY id
子句=]查询。
请注意,(status,id)
上订单 table 的索引将有助于优化查询以将订单放入批次中并从批次中释放它们。