1

我有一个 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

查询的目标是插入大量相关数据(基于 . 的probact_actionstaken自动值存储index在)。@actionTakenIdindextimestamp

该查询在大多数情况下都能正确执行:这五个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语句的结果,该结果存储在 my $output.

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

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

MySQL 或 PHP 中是否有某些原因可以防止出现竞争条件问题?我可以采取一些步骤吗?或者有没有一种安全的方法来使用多个准备好的语句?如果是后者,它是否适用于 PHP 3.5.10?

4

1 回答 1

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(和所有软件)也被认为是明智的。

于 2013-08-26T20:50:10.547 回答