【问题标题】:Multiple instances of same PHP script processing diffrent MySQL rows using rowlocking同一 PHP 脚本的多个实例使用行锁定处理不同的 MySQL 行
【发布时间】:2013-02-07 20:02:09
【问题描述】:

我想做的是用 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 是否响应,并在数据库中更新其状态(和时间戳)

【问题讨论】:

  • 你不能使用事务吗?
  • 这些行是否相互独立?如果您有一个主脚本组成一个要处理的行列表,然后将该列表分割成一堆单独的子脚本来进行处理,可能会更好。
  • 您是否害怕每 5 分钟运行的实例会持续超过 5 分钟,或者您想显式运行 X 个实例?

标签: php mysql innodb rowlocking


【解决方案1】:

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

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

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

找工作来处理

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

SELECT id FROM jobs ORDER BY created_at ASC LIMIT 1

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

找工作

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

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 &lt; NOW())。然后,申请工作还需要在选择时和使用 SELECT ... FOR UPDATE 申请时检查工作行是否有申请 (claim_expires_at IS NULL)。

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 如果您要投反对票,请礼貌地解释原因。
    • 1) 还有一点机会,同一行会被读取两次。 2)万一,脚本崩溃?行保持处理状态...毕竟我想我会在选择后更新timestamp,因为我是根据timestamp &lt;= yesterday选择的
    猜你喜欢
    • 2021-11-03
    • 1970-01-01
    • 2015-10-31
    • 2017-08-16
    • 2022-07-30
    • 2021-07-01
    • 1970-01-01
    • 2020-12-24
    相关资源
    最近更新 更多