问题描述
'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
之前,另一个队列工作人员可以拿起我刚刚选择的项目。我该如何阻止这种情况发生?
解决方法
您可以在单个查询中执行此操作。将update
与行限制子句一起使用:
UPDATE `queue_items`
SET `processed_at` = @processedAt
WHERE `processed_at` IS NULL AND `completed_at` IS NULL
ORDER BY id LIMIT 1
如果要保留已更新的行的id
,这有点棘手。 MySQL不支持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;
,
围绕两个查询放置一个事务,并在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;