背景
我有一个包含两个或多个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'
。它会自动重定向到自身并重新启动整个过程。
我遇到的挑战:
如果我想同时运行2个或更多相同的PHP脚本同时访问同一个数据库,我该如何确保他们不访问同一个记录?
更大的挑战是,如果我想在同一个PHP脚本的多个实例上将循环从start
拆分为end
,即每100个值是一个实例脚本,在到达1100
后,将MySQL Start
更新为1100
并重定向到从1101
到1200
开始的相同脚本,依此类推达到了End
个数字。达到End
后,该记录的Status
将设置为1
,并且会在下一条记录上移动。在实际情况中,它从开始到结束大约有一百万个计数,并且一些内存密集型函数在循环中执行,所以我需要把它分块。
关于行级锁定的所有知识,我在最后几分钟学到了;并且似乎没有回答来自一个脚本的多个实例锁定。
我应该考虑会话ID / cookie吗?你推荐什么?
答案 0 :(得分:4)
我建议:
使用支持行级锁定的InnoDB
:
ALTER TABLE `table` ENGINE=InnoDB;
使用值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;
在客户端中使用交易:
$dbh->setAttribute(PDO::ATTR_AUTOCOMMIT, FALSE); // pdo
$mysqli->autocommit(FALSE); // mysqli (OOP)
mysqli_autocommit($link, FALSE); // mysqli (procedural)
mysql_query('START TRANSACTION'); // ext/mysql *
使用locking read获取要处理的行:
SELECT * FROM `table` WHERE status_new = 'queued' LIMIT 1 FOR UPDATE;
将提取的行的status
设置为'in progress'
:
UPDATE `table` SET status_new = 'in progress' WHERE ID = ?
提交交易:
$dbh->commit(); // pdo
$mysqli->commit(); // mysqli (OOP)
mysqli_commit($link); // mysqli (procedural)
mysql_query('COMMIT'); // ext/mysql *
处理完成后,再次UPDATE
记录,以便将其标记为。
请注意,您正确检测并处理了每个阶段出现的任何错误。
*请注意,自2011年6月起,建议不要将ext/mysql
用于新代码,现在已在PHP v5.5中弃用。