首页 > 解决方案 > 冲突的交易导致奇怪的行为

问题描述

我有一个记录销售交易的应用程序。在此过程中,会更新和记录库存量。这些查询作为事务运行。但是,如果其中 2 个事务同时运行;库存计数和记录可能不正确。

下面是一个查询示例。如果它们同时运行,第二个查询可能会看到与第一个查询相同的值,导致只有 1 个事务来正确更新数量。事务日志将包含两个条目,但如果它们同时运行,当前数量将是错误的。

确保这些事务以不会导致不一致的方式运行的最佳方法是什么

INSERT INTO transaction_log(item_id, current_quantity, amount_changed) VALUES (1, 10,-1);
INSERT INTO inventory (current_quantity, item_id) VALUES (10, 1) ON DUPLICATE KEY UPDATE quantity = 10

标签: mysqltransactions

解决方案


在 的更新中inventory,我们可以对值进行调整,而不是指定替换值

(我们假设这个问题不是指插入transaction_log表的问题;向该表添加行可以正常工作。我们假设问题在于inventory表的更新)

我们将假设current_quantity列列表中引用的列是来自交易的数量,而quantity更新部分中引用的列是运行值。

为了这个例子,我将把它们称为数量“调整”和“现有”。

假设表格行当前如下所示:

 item_id  quantity_onhand  quantity_adjust
 -------  ---------------  ---------------
       1              100               20

当前的现有库存为 100,最后应用的调整为 20。

假设我们需要调整quantity_onhand10 的值。我们可以这样做:

INSERT INTO inventory (item_id, quantity_adjust) VALUES (1, 10)
ON DUPLICATE KEY
UPDATE quantity_onhand = quantity_onhand + VALUES(quantity_adjust)
     , quantity_adjust =                   VALUES(quantity_adjust)

假设 item_id 是插入违反的主键(或唯一键),我们期望结果是:

 item_id  quantity_onhand  quantity_adjust
 -------  ---------------  ---------------
       1              110               10

即,将 10 添加到“现有”数量,并将 10 存储为最后一个“调整”值。

请注意,在语句的更新部分VALUES(),如果插入成功,该函数将返回将被插入的值。

结果实际上等同于执行以下语句:

UPDATE inventory
   SET quantity_onhand = quantity_onhand + 10
     , quantity_adjust =                   10
 WHERE item_id = 1

因为该语句只提供一个调整值,而不是一个特定的现有值,即从行本身获取当前库存值,所以在该行被语句锁定时,这会处理多个语句尝试替换的并发问题现有库存量。


请注意,我们还需要处理INSERT语句成功、行不存在的情况。我们可以将该quantity_onhand列定义为 `DEFAULT 0(以避免存储 NULL 值),然后我们会得到一个存储为现有量的零,这可能不是我们想要的。


推荐阅读