在MySQL中同时选择和更新表的行?

时间:2015-09-21 03:22:54

标签: mysql

我已经完成了几乎所有类似的问题,但没有找到我的问题的答案。很抱歉,如果我错过了已经发布的问题,那么我会回答这个问题。

我有一个MySQL表,我将其用作作业队列。有多个工作人员从这些表中读取工作。

挑战在于如何使用桌面上的MySQL查询来实现这一目标。

我必须选择行并同时更新作业状态。这应该是自动的,因此没有工人获得已处理的工作。

我想自动运行(伪代码):

select name, job_type from jobs where job_status = "created" limit 10;

foreach row {
    update table jobs set job_status = "processing" where id = '$id';
}

这是否可以在MySQL中使用查询/存储过程/游标?

2 个答案:

答案 0 :(得分:0)

我相信这是您案件的解决方案:

update jobs set job_status = "processing"
where id = '$id'
and job_status = "created"

答案 1 :(得分:0)

这些技术依赖于事务,因此请确保自动提交已关闭。出于性能和数据完整性的原因,您无论如何都不应该使用自动提交。

这也依赖于使用InnoDB表格式。 MyISAM不支持行级锁定。

使用SELECT ... FOR UPDATE对返回的行进行独占锁定。在事务提交或回滚之前,锁将保持不变。

select id, name, job_type
from jobs
where job_status = "created"
limit 10
for update;

foreach row {
    update table jobs set job_status = "processing" where id = '$id';
    ...process...
    delete from jobs where id = '$id';
}

commit

除非你有充分的理由这样做,否则你最好一次只抓一排。这是一个简单,快速的查询,没有理由坚持10.实际上,如果你保持10,你就不能在每次成功的工作之后提交。

select id, name, job_type
from jobs
where job_status = "created"
limit 1
for update;

update table jobs set job_status = "processing" where id = '$id';
...process the job...
delete from jobs where id = '$id';

commit

我们可以进一步削减它。没有必要将工作设置为处理,其他交易无论如何都不会看到更改。我们可以依靠独家锁。

select id, name, job_type
from jobs
where job_status = "created"
limit 1
for update;

...process $id...

delete from jobs where id = '$id';

commit

这很强大。如果您的工作人员崩溃,其锁定将被删除,另一名工作人员可以再次尝试该工作。

或者,您可以一次更新一个。这需要getting the ID of the row you just updated

SET @update_id := 0;

UPDATE jobs
SET job_status = "processing", id = (SELECT @update_id := id)
WHERE job_status = "created"
LIMIT 1;

SELECT @update_id;

...do work on @update_id...

DELETE FROM jobs WHERE id = @update_id

COMMIT

这依赖于UPDATE setting an exclusive lock on each updated row,其他交易将无法更新该行。这就是为什么一次一个工作的好主意。

或者,添加queued作业状态以及哪个进程拥有它。

UPDATE jobs
SET job_status = "queued", job_owner = "$pid"
WHERE job_status = "created" limit 10;

COMMIT;

SELECT name, job_type
FROM jobs
WHERE job_status = "queued"
  AND job_owner = "$pid"

foreach row {
    UPDATE jobs SET job_status = "processing" where id = '$id';
    ... process ...
    DELETE FROM jobs WHERE id = '$id';
    COMMIT;
}

这种方法的缺点是,如果工人死亡,它仍将拥有工作。如果你有一个长寿命的主人控制队列并将工作分配给工人,我只会推荐这种方法。

最后,有subtle problems with using MySQL as a queue。考虑获得真正的排队服务。