0

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

目前我首先这样做是为了获取一些 URL:

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

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

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

我非常清楚这是多么愚蠢和低效。更重要的是,不能保证另一个工作进程不会尝试在我的 UPDATE 中间获取列表。这样做的美丽方法是什么?我是否应该进行交易,如何?

4

2 回答 2

2

以下出现(通过快速浏览 MySQL 5 手册)在 MySQL 中可用;我不确定这是否是最好的方法,但我之前在 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 的扩展。

我在您的方法中看到的一个潜在问题是 URL 重复:如果 urlhttp://www.example.com/在您的表中出现两次,例如 ID 为 23 和 42,则 SELECT 将返回这两个 ID 之一,但 UPDATE 会影响这两行。我不知道这种行为在您的应用程序中是否有意义;我可能会对 URL 施加某种 UNIQUE 约束,这样它就不会发生,然后在IN子句中使用 ID 列表,而不是 URL(应该更快)。

于 2009-03-20T02:22:10.853 回答
0

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

于 2009-03-20T02:21:21.043 回答