选择&同时更新 - 竞争条件

时间:2014-08-28 22:52:16

标签: mysql

我遇到了竞争条件,因为我正在处理大量的并发问题。 我正在尝试将这两个mysql语句组合在一起同时执行。

我需要选择一行并更新同一行......

SELECT id_file FROM filenames WHERE pending=1 LIMIT 1;
UPDATE filenames SET pending=2 WHERE id_file=**id of select query**;

我遇到的竞争条件的另一个解决方案是执行UPDATE查询,其中pending = 1并以某种方式获取更新行的ID,但我不确定这是否可能?

由于

2 个答案:

答案 0 :(得分:1)

您可以通过在表上只执行UPDATE语句来避免“竞争”条件,允许它识别要修改的行,然后从行中检索列的值。

在您的情况下,有一个“技巧”返回列的值,刚刚更新的行中的id_file列的值。您可以使用 LAST_INSERT_ID() 函数(仅当列为整数类型时)或MySQL 用户定义变量

如果要检索的列的值是整数,则可以使用LAST_INSERT_ID()函数(支持BIGINT-64值)。

例如:

UPDATE filenames 
   SET pending = 2 
     , id_file = LAST_INSERT_ID(id_file)
 WHERE pending = 1
 LIMIT 1;

成功执行UPDATE语句后,您需要验证至少有一行受到影响。 (如果任何行满足WHERE,并且语句成功,我们知道一行将受到影响。然后您可以在同一会话中检索该值:

SELECT LAST_INSERT_ID();

检索UPDATE语句处理的最后一行的id_file列的值。请注意,如果UPDATE处理多行,则只有UPDATE处理的 last 行的值可用。 (但这对你来说不是问题,因为有一个LIMIT 1条款。)

同样,在依赖LAST_INSERT_ID()函数返回的值之前,您需要确保实际更新了一行。


对于非整数列,您可以以类似的方式使用MySQL用户定义的变量,将列的值分配给用户定义的变量,然后立即检索存储在用户定义的变量中的值。

-- initialize user-defined variable, to "clear" any previous value
SELECT @id_file := NULL;

-- save value of id_file column into user-defined variable
UPDATE filenames 
   SET pending = 2 
     , id_file = (SELECT @id_file := id_file)
 WHERE pending = 1
 LIMIT 1;

-- retrieve value stored in user-defined variable
SELECT @id_file;

请注意,此变量的值在会话中保留。如果UPDATE语句没有找到满足谓词(WHERE子句)的任何行,则用户定义变量的值将不受影响...因此,为了确保您不会无意中获得“旧”值,您可能希望首先使用NULL初始化该变量。

请注意,后续触发的触发器不会修改该用户定义变量的值。 (用户定义的变量在当前会话中是“范围内”。)

也可以在触发器中对用户定义的变量进行赋值,但我不会证明这一点,并且我建议你在触发器中进行。

答案 1 :(得分:1)

处理并发是transactions的基本功能之一。

将您的查询包装到一个事务中并告诉DBMS,您需要在FOR UPDATE之间不要更改行:

BEGIN;
SELECT id_file FROM filenames WHERE pending=1 LIMIT 1 FOR UPDATE;
# do whatever you like
UPDATE filenames SET pending=2 WHERE id_file=**id of select query**;
COMMIT;

您可以通过4次mysqli_query调用执行这些语句,并在其间执行任何操作,而无需担心数据库的一致性。选定的行将保存,直到您将其释放。