从两个或多个脚本访问/更新一个表的最佳方法?

时间:2013-05-04 05:28:21

标签: php mysql

背景

我有一个包含两个或多个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脚本的多个实例上将循环从start拆分为end,即每100个值是一个实例脚本,在到达1100后,将MySQL Start更新为1100并重定向到从11011200开始的相同脚本,依此类推达到了End个数字。达到End后,该记录的Status将设置为1,并且会在下一条记录上移动。在实际情况中,它从开始到结束大约有一百万个计数,并且一些内存密集型函数在循环中执行,所以我需要把它分块。

  3. 关于行级锁定的所有知识,我在最后几分钟学到了;并且似乎没有回答来自一个脚本的多个实例锁定。

    我应该考虑会话ID / cookie吗?你推荐什么?

1 个答案:

答案 0 :(得分:4)

我建议:

  1. 使用支持行级锁定的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. 使用locking read获取要处理的行:

    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记录,以便将其标记为。

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

    *请注意,自2011年6月起,建议不要将ext/mysql用于新代码,现在已在PHP v5.5中弃用。