【发布时间】:2013-08-26 20:43:56
【问题描述】:
我有一个 PHP 脚本,它生成一个类似于下面的 MySQL 查询:
INSERT INTO `dailydb`.`probact_actionstaken`(`dispatcher`) VALUES ('kryan');
SET @actionTakenId = LAST_INSERT_ID();
INSERT INTO `dailydb`.`probact_actionstaken_types`(`actionTaken`,`type`)
VALUES
(@actionTakenId, 2);
INSERT INTO `dailydb`.`probact_actionstaken_problems`(`actionTaken`,`problem`)
VALUES
(@actionTakenId, 2);
INSERT INTO `dailydb`.`probact_actionstaken_actions`(`actionTaken`,`action`)
VALUES
(@actionTakenId, 3);
INSERT INTO `dailydb`.`probact_actionstaken_text`(`actionTaken`,`input`,`value`)
VALUES
(@actionTakenId, 3, '7576'),
(@actionTakenId, 4, 'B61'),
(@actionTakenId, 5, '4'),
(@actionTakenId, 6, 'kryan'),
(@actionTakenId, 7, 'test'),
(@actionTakenId, 8, 'testing new debug');
SELECT `index`, `timestamp`
FROM `dailydb`.`probact_actionstaken`
WHERE `index`=@actionTakenId;
(请注意,为了显示此查询,所有值都是在 :someId 上手动搜索并替换为相应值的结果,因为我使用的是准备好的语句)
(还要注意probact_actionstaken.index是一个自动递增的主键,probact_actionstaken.timestamp默认为CURRENT_TIMESTAMP)
查询的目标是插入大量相关数据(基于@actionTakenId中存储的probact_actionstaken.index的自动值),然后返回自动生成的index和timestamp 供使用(在使用此输出的 AJAX 调用中调用脚本)。
此查询在大多数情况下都正确执行:五个INSERT 语句都在数据库中插入了正确的值。我的问题是最后一个SELECT 声明。我尝试使用以下 PHP 代码查看查询结果:
$statement = $dbh->prepare($query);
$statement->execute($params);
$output['results'] = array();
do
{
$output['results'][] = array();
while ( $result = $statement->fetch(PDO::FETCH_ASSOC) )
{
$output['results'][count($output['results'])-1][] = $result;
if ( isset($result['index']) )
{
$output['index'] = intval($result['index']);
}
if ( isset($result['timestamp']) )
{
$output['timestamp'] = $result['timestamp'];
}
}
} while ( $statement->nextRowset() );
echo json_encode($output);
这个想法是使用do...while 块来获取来自INSERT 语句的结果,然后获取最终的SELECT 语句的结果,该语句存储在我的$output 中。
在我的本地机器上(运行 PHP 5.4.4),这可以完美运行。在具有 PHP 5.3.10 的服务器上,$statement->nextRowset() 每次都返回 false,所以我只得到第一个行集(它是空的,因为它来自 INSERT 语句)。请注意,无论如何,实际插入都可以正常工作,并且当我在 phpMyAdmin 的 SQL 功能中运行上述手动填写的查询时,它会返回正确的结果(由插入的索引和时间戳组成的表)。
阅读这些问题后,似乎像这样的多个语句是一个坏主意,并且实际上不适用于真正的准备语句,而且我仍在使用默认的模拟准备语句。所以我可以切换到 $dbh->prepare 和 $statement->execute 的单独调用,这将解决我的问题,但我担心竞争条件。服务器正在使用 Nginx,并且可能有许多用户同时输入条目。我不希望 LAST_INSERT_ID() 返回其他用户的条目并将这些字段与错误的操作相关联。
在 MySQL 或 PHP 中是否有某些原因可以防止出现竞争条件问题?我可以采取一些步骤吗?或者有没有一种安全的方法来使用多个准备好的语句?如果是后者,它是否适用于 PHP 3.5.10?
【问题讨论】:
标签: php mysql pdo prepared-statement race-condition