1

我想要做的是每隔几分钟使用 cron 执行相同的脚本。

该脚本需要处理从数据库读取的一些数据,所以显然我需要它每次都在不同的行上工作。

我的概念是使用行锁定来确保每个实例在不同的行上工作,但它似乎不是那样工作的。甚至可以以这种方式使用行锁吗?还有其他溶液吗?

例子:

while($c < $limit) {
   $sql=mysql_query("SELECT * FROM table WHERE ... LIMIT 1 FOR UPDATE");
   $data=mysql_fetch_assoc($sql);

   (process data)

   mysql_query("update table set value=spmething, timestamp=NOW()");
   $c++;
}

基本上我需要的是 SCRIPT1 从表中读取 R1;SCRIPT2 读取 R2(下一个非锁定行匹配条件)

编辑:例如:1)表存储 URL 列表 2)脚本检查 URL 是否响应,并在数据库中更新它的状态(和时间戳)

4

2 回答 2

4

这基本上应该被视为两个独立的问题:

  1. 为每个工人寻找一份工作来处理。理想情况下,这应该非常有效,并在接下来的第 2 步中预先避免失败。
  2. 确保每个作业最多处理一次或仅处理一次。无论发生什么情况,同一个作业都不应该由多个工人同时处理。您可能希望确保不会因为有车/撞车的工人而失去工作。

这两个问题都有多种可行的解决方案。我会就我的偏好给出一些建议:

找工作来处理

对于低速系统,只需查找最近的未处理作业就足够了。您还不想接受这份工作,只需将其确定为候选人即可。这可能是:

SELECT id FROM jobs ORDER BY created_at ASC LIMIT 1

(请注意,这将首先处理最旧的作业——先进先出顺序——并且我们假设行在处理后被删除。)

申请工作

在这个简单的例子中,这将很简单(注意我正在避免一些可能使事情变得不太清楚的优化):

BEGIN;
SELECT * FROM jobs WHERE id = <id> FOR UPDATE;
DELETE FROM jobs WHERE id = <id>;
COMMIT;

如果SELECT查询时返回我们的工作id,我们现在已经锁定它。如果另一个工人已经接受了这个工作,则会返回一个空集,我们应该寻找另一个工作。如果两个工人竞争同一个工作,他们会从一开始就互相阻止SELECT ... FOR UPDATE,这样前面的陈述就普遍成立了。这将允许您确保每个作业最多处理一次。然而...

只处理一次作业

先前设计中的一个风险是工作人员接受了一项工作,但未能处理它,然后崩溃。现在失去了这份工作。因此,大多数作业处理系统在他们声称作业时不会删除作业,而是将其标记为由某个工人声称并实施作业回收系统。

job这可以通过使用表中的附加列或单独的表来跟踪索赔本身来实现claim。通常会写一些关于工作人员的信息,例如主机名、PID 等,( claim_description) 并为声明提供一些到期日期 ( claim_expires_at),例如未来 1 小时。然后,一个额外的过程会处理这些索赔,并以事务方式释放过期的索赔 ( claim_expires_at < NOW())。claim_expires_at IS NULL然后,申请工作还需要在选择时和申请时检查工作行是否有申请 ( ) SELECT ... FOR UPDATE

请注意,这个解决方案仍然存在问题:如果一个作业处理成功,但工人在成功将作业标记为已完成之前崩溃了,我们最终可能会释放索赔并重新处理该作业。解决这个问题需要一个更高级的系统,作为练习留给读者。;)

于 2013-02-21T01:34:53.860 回答
1

如果您要读取该行一次,并且只读取一次,那么我将创建一个is_processed列并简单地更新您已处理的行上的该列。然后您可以简单地查询具有is_processed = 0

于 2013-02-07T20:19:36.107 回答