0

背景

我有一个表,其中包含两个或多个 PHP 脚本将同时访问的数据。数据看起来像这样:

+--------+-------------------+-----------+-----------+
| ID     | Start             |  End      |  Status   |
+--------+-------------------+-----------+-----------+
| 1      | 1000              | 1500      |  0        |
| 2      | 1000              | 1500      |  0        |
| 3      | 1000              | 1500      |  0        |
| 4      | 1000              | 1500      |  0        |
+--------+-------------------+-----------+-----------+

PHP 脚本在启动时执行以下查询:

SELECT * FROM `table` WHERE `Status` = 0 LIMIT 1

然后 PHP 脚本执行从start值到end值(1000 到 1500)的循环。完成后,它Status将该行的 更新为'1'。它会自动重定向到自身并重新启动整个过程。

我面临的挑战是

  1. 如果我希望 2 个或更多相同的 PHP 脚本同时运行访问同一个数据库,我如何确保它们没有访问相同的记录?

  2. 更大的挑战是,如果我想将循环从同一个 PHP 脚本的多个实例拆分startend多个实例,即每 100 个值是脚本的一个实例,并且在到达之后1100,更新 MySQL并重定向Start1100同一个脚本从1101to开始1200,以此类推,直到End达到数量。达到该End数字后,该记录Status将被设置为1并移至下一条记录。在实际情况下,它从开始到结束大约有一百万个计数,并且在循环中执行了一些内存密集型功能,因此我需要将其分块。

我所知道的关于行级锁定的一切,都是在最后几分钟里学到的;而且这些似乎都不能从一个脚本中回答多个实例锁定问题。

我应该考虑会话 ID/cookie 吗?你有什么建议吗?

4

1 回答 1

4

我会建议:

  1. Using InnoDB,它支持行级锁定:

    ALTER TABLE `table` ENGINE=InnoDB;
    
  2. 使用值Status来表示“进行中”;更明确一点,让我们使用 MySQL 的ENUM类型:

    ALTER TABLE `table`
      ADD status_new ENUM('queued','in progress','complete') AFTER status;
    
    UPDATE `table` SET status_new = CASE status
      WHEN 0 THEN 'queued'
      WHEN 1 THEN 'complete'
    END;
    
    ALTER TABLE `table` DROP status;
    
  3. 在客户端使用事务:

    $dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, FALSE);   // pdo
    $mysqli->autocommit(FALSE);                        // mysqli (OOP)
    mysqli_autocommit($link, FALSE);                   // mysqli (procedural)
    mysql_query('START TRANSACTION');                  // ext/mysql *
    
  4. 使用锁定读取来获取要处理的行:

    SELECT * FROM `table` WHERE status_new = 'queued' LIMIT 1 FOR UPDATE;
    
  5. status获取的行的设置为'in progress'

    UPDATE `table` SET status_new = 'in progress' WHERE ID = ?
    
  6. 提交事务:

    $dbh->commit();                                    // pdo
    $mysqli->commit();                                 // mysqli (OOP)
    mysqli_commit($link);                              // mysqli (procedural)
    mysql_query('COMMIT');                             // ext/mysql *
    
  7. 处理完成后,UPDATE再次记录以将其标记为此类。

请注意,您正确检测和处理每个阶段出现的任何错误。

* 请注意,ext/mysql自 2011 年 6 月以来,不建议在新代码中使用它,现在在 PHP v5.5 中已弃用。

于 2013-05-04T06:48:28.303 回答