PDO,多种陈述和竞争条件

时间:2013-08-26 20:43:56

标签: php mysql pdo prepared-statement race-condition

我有一个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_actionstakenindex是自动递增的主键,probact_actionstakentimestamp默认为CURRENT_TIMESTAMP

查询的目标是插入大量相关数据(基于存储在probact_actionstaken中的index@actionTakenId的自动值),然后自动返回生成indextimestamp以供使用(在使用此输出的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语句的结果,该语句存储在我的{ {1}}。

在我的本地计算机上(运行PHP 5.4.4),这非常有效。在具有PHP 5.3.10的服务器上,$output每次都返回false,因此我只获取第一个行集(由于它来自$statement->nextRowset()语句而为空)。请注意,实际插入无论如何都能正常工作,当我在phpMyAdmin的SQL功能中运行上面的手动填充查询时,它会返回正确的结果(由插入的索引和时间戳组成的表)。

阅读这些问题,似乎这样的多个语句都是一个坏主意,并且实际上并没有使用真正准备好的语句,我仍然使用默认的模拟预处理语句。所以我可以切换到INSERT$dbh->prepare的单独调用,这将解决我的问题,但我担心竞争条件。服务器正在使用Nginx,许多用户可能同时输入条目。我不希望$statement->execute返回其他用户的条目,并将这些字段与错误的操作相关联。

在MySQL或PHP中是否有某些原因可以防止竞争条件问题?我可以采取一些措施?或者是否有一种安全的方式来使用多个准备好的语句?如果是后者,它是否可以在PHP 3.5.10中运行?

1 个答案:

答案 0 :(得分:1)

  

我不希望LAST_INSERT_ID()返回其他用户的条目,并将这些字段与错误的操作相关联。

LAST_INSERT_ID()函数仅返回在当前会话中生成的最新ID,而不是另一个MySQL会话。

请参阅http://dev.mysql.com/doc/refman/5.6/en/information-functions.html#function_last-insert-id

  

生成的ID基于每个连接在服务器中维护。这意味着函数返回给定客户端的值是为该客户端影响AUTO_INCREMENT列的最新语句生成的第一个AUTO_INCREMENT值。此值不受其他客户端的影响,即使它们生成自己的AUTO_INCREMENT值。此行为可确保每个客户端都可以检索自己的ID,而无需关心其他客户端的活动,也无需锁定或事务。

由于竞争条件的可能性,如果不以这种方式限制范围,该功能将毫无用处。

我不知道你在处理PHP 5.3和PHP 5.4之间的多结果查询时所看到的差异的答案。避免多查询被认为是最佳做法。

在您将在生产中部署的开发人员环境中使用相同版本的PHP(以及所有软件)也是明智之举。