1

I have 5 cron jobs running a PHP file. The PHP file checks the MySQL database for items that require processing. Since cron launches the scripts all at the same time, it seems that some of the items are processed twice, or even sometimes up to five times.

Upon SELECting the file in one of the scripts, it immediately sends an UPDATE query so that other jobs shouldn't run it again. But looks like it's still double processing.

What can I do to prevent the other scripts from processing an item that was previously selected by the other cron jobs?

4

3 回答 3

4

这个问题被称为“竞争条件”。在这种情况下,这是因为 SELECT 和 UPDATE 虽然一个接一个地被调用,但并不是一个单一的操作。因此,有可能两个作业执行 SELECT 同一个作业,然后首先执行 UPDATE,然后再执行 UPDATE。所以他们继续同时运行这项工作。

但是,有一种解决方法。您可以在表中添加一个字段,其中包含当前 cron 作业工作者的 ID(如果您在一台机器上运行它,它可能是 PID)。在工作人员中,您首先执行 UPDATE ,尝试为其保留工作:

UPDATE jobs 
    SET worker = $PID, status = 'processing' 
    WHERE worker IS NULL AND status = 'awaiting' LIMIT 1

然后您验证您已成功为该工作人员保留了工作:

SELECT * FROM jobs WHERE worker = $PID

如果它没有返回一行,则表示其他工人先保留它。您可以从第 1 步再试一次以获取另一份工作。如果它确实返回了一行,您将完成所有处理,然后最后进行最终更新:

UPDATE jobs 
    SET status = 'done', worker = NULL
    WHERE id = $JOB_ID
于 2013-08-03T09:46:16.863 回答
2

我认为你有一个使用信号量的典型问题。看看这篇文章:

http://www.re-cycledair.com/php-dark-arts-semaphores

这个想法首先是每个脚本,要求相同的信号量并等到它空闲。然后在执行此操作时选择并更新数据库,释放信号量并启动该过程。这是唯一可以确保只有一个脚本正在读取数据库,而另一个脚本即将在其上写入的唯一方法。

于 2013-08-03T09:47:03.117 回答
0

我会重新开始。这个思路:

处理一件物品需要时间。大约 30 秒。如果我有五个 cron 作业,则在 30 秒内处理五个项目

这是完全错误的,你不应该在编写代码时考虑到这一点。

按照这个逻辑,为什么不做 100 个 cron 作业并每 30 秒做 100 个呢?回答,因为您的服务器不是RoadRunner,它会翻倒和失败。

你应该

  1. 重新考虑您的问题,这是最重要的,因为它将有助于解决 1 和 2。
  2. 优化您的代码,使其不需要 30 秒。
  3. 分段您的代码,以便每个作业一次只执行一项任务,这将使其更快,并确保您不会获得这种“双重处理”效果。

编辑

即使有了在第三方服务器上的新知识,我的逻辑仍然成立,不要启动您无法控制的多个调用,事实上这现在更加重要。

如果您不知道他们对这些呼叫做了什么,那么您就无法确定它们的顺序是否正确,何时或是否处理它们。因此,只需拨打一个电话,以确保您不会得到双重处理。

技术解决方案是让他们缩短处理时间或让您缓存响应 - 但这可能与您的情况无关。

于 2013-08-03T09:27:32.700 回答