提问人:async life 提问时间:8/31/2020 最后编辑:GMBasync life 更新时间:9/4/2020 访问量:513
锁定MySQL选择行,直到在其上运行UPDATE?
Lock MySQL select row until UPDATE has been ran on it?
问:
我有一个名为 3 列的表。queue_items
'item' varchar(500) the item that has been queued
'processed_at' a datetime to mark when it has started processing, and
'completed_at' a datetime to mark when it has completed in the queue worker
这是我选择项目的方式,
SELECT `id`,`item` FROM `queue_items` WHERE `processed_at` IS NULL AND `completed_at` IS NULL ORDER BY `id` ASC LIMIT 1
一旦检查它存在并且可以有效处理,我就会运行它:
UPDATE `queue_items` SET `processed_at` = @processedAt WHERE `id` = @id
问题是,在我更新 之前,另一个队列工作线程可以选取我刚刚选择的项目。我怎样才能阻止这种情况的发生?processed_at
答:
0赞
GMB
8/31/2020
#1
您可以在单个查询中执行此操作。与行限制子句一起使用:update
UPDATE `queue_items`
SET `processed_at` = @processedAt
WHERE `processed_at` IS NULL AND `completed_at` IS NULL
ORDER BY id LIMIT 1
如果要保留已更新的行,则有点棘手。MySQL不支持该子句,但我们可以使用用户变量来解决此问题:id
RETURNING
SET @updated_id := 0;
UPDATE `queue_items`
SET `processed_at` = @processedAt, id = (@x := id)
WHERE `processed_at` IS NULL AND `completed_at` IS NULL
ORDER BY id LIMIT 1;
@SELECT @updated_id;
评论
0赞
Josh Hallow
8/31/2020
当然可以,但是如何获取同时更新的记录项目?否则,我将不知道要在工作线程上处理哪个项目。如果我使用对上次更新的 ID 的查询并以这种方式获取它,那么另一个工作人员可能会更新记录,因为这会让我处于同一位置。
0赞
Josh Hallow
8/31/2020
谢谢!那么,这是否不允许两个工作线程获取相同的队列项目,即使使用多核MySQL服务器也是如此?
0赞
GMB
8/31/2020
@asynclife:从并发的角度来看,这是安全的,因为操作是在单个语句中执行的(而不是在最初的尝试中执行两个) - 当然,用户变量是会话私有的。
1赞
Josh Hallow
8/31/2020
更新:通过更改为正确的变量名称来修复它@x
1赞
Josh Hallow
8/31/2020
对于使用 C# 并偶然发现此答案的任何人,请确保在连接字符串中设置为 true,否则由于未定义参数,它将引发异常。AllowUserVariables
0赞
Barmar
9/4/2020
#2
在两个查询周围放置一个事务,并使用查询中的选项锁定它检查的行。尝试读取该行的任何其他连接都将被挂起,直到事务提交为止。FOR UPDATE
SELECT
确保在子句中测试的列上有索引,这样它就不必在找到所需的行之前进行扫描并锁定它检查的所有行。WHERE
START TRANSACTION;
SELECT @id := `id`,`item`
FROM `queue_items`
WHERE `processed_at` IS NULL AND `completed_at` IS NULL
ORDER BY `id` ASC
LIMIT 1
FOR UPDATE;
UPDATE `queue_items` SET `processed_at` = @processedAt WHERE `id` = @id
COMMIT;
评论
0赞
Josh Hallow
9/4/2020
我在你的答案中没有看到这个选项,我应该把它放在哪里?此外,如果第二个实例尝试运行相同的查询,它是否只获取下一行?我是否需要在我的应用程序中添加任何条件检查才能计算在内?FOR UPDATE
0赞
Barmar
9/4/2020
你没有在之后看到它吗?WHERE
0赞
Barmar
9/4/2020
实际上,那是错误的地方。它必须在.LIMIT 1
0赞
Josh Hallow
9/4/2020
对不起,我在您更新问题之前发布了它(或者我可能错过了它)。你能回答我的后一个问题吗?
0赞
Barmar
9/4/2020
在事务完成之前,第二个实例将被阻止。届时,此行将更新,并获取下一行。
评论