使用MySQL将任务分配给工作进程的正确方法

时间:2009-03-20 02:13:40

标签: sql mysql transactions innodb

我在MySQL InnoDB表中有一个庞大的URL列表,以及查询MySQL以查找要处理的一组URL的工作进程。应立即将URL标记为正在处理,以便其他工作进程不会通过开始处理相同的资源来浪费资源。

目前我首先这样做以获取一些网址:

SELECT DISTINCT url FROM urls WHERE task_assigned is NULL ORDER BY id LIMIT 100

然后在代码中我天真地遍历每个网址以将其标记为正在处理:

UPDATE urls SET task_assigned = NOW() WHERE url = ? COLLATE utf8_bin

我完全清楚这是多么愚蠢和低效。更重要的是,不能保证另一个工作进程不会尝试在我的UPDATE中间获取列表。这样做的美妙方式是什么?我应该把它作为交易,怎么做?

2 个答案:

答案 0 :(得分:2)

在MySQL中可以看到以下内容(通过快速浏览MySQL 5手册);我不确定它是否是最好的方法,但是我以前在PostgreSQL中使用过的方法:

BEGIN TRANSACTION;
SELECT DISTINCT url FROM urls WHERE task_assigned is NULL ORDER BY id LIMIT 100 FOR UPDATE;
UPDATE urls SET task_assigned = NOW() WHERE url IN [list of URLs] COLLATE utf8_bin;
COMMIT;

实际上在PostgreSQL中,我会使用 UPDATE语句,UPDATE的RETURNING子句代替SELECT,但这是PostgreSQL特有的扩展名。

我在您的方法中看到的一个潜在问题是重复的网址:如果网址http://www.example.com/在您的表格中出现两次,例如ID为23和42,则会通过SELECT返回其中一个ID,但是UPDATE将影响两行。我不知道这种行为在你的申请中是否有意义;我可能会对URL设置某种UNIQUE约束,因此不会发生这种情况,然后在IN子句中使用ID列表而不是URL(应该更快)。

答案 1 :(得分:0)

也许您应该先选择所有的URL,然后使用线程异步解析它们?