我正在编写一个PHP类文件,它使用PDO将数据推送到MySQL数据库.本质上,文件很快被多次命中(每次都创建一个新的类实例),并且lastInsertId()方法没有跟上.例如:
//sleep(rand(100,1000)/100); $sql = "INSERT INTO `testing` (`name`, `timestamp`) VALUES (?, ?)"; $this->dbh->beginTransaction(); $sth = $this->dbh->prepare($sql); $sth->bindValue(1, $_POST["name"]); $sth->bindValue(2, microtime()); $sth->execute(); $this->id = $this->dbh->lastInsertId(); $this->dbh->commit();
如果页面被非常快速地调用两次,$this->id
则返回时两个实例的值都为2,尽管DB看起来像这样:
+----+--------+-----------------------+ | id | name | timestamp | +----+--------+-----------------------+ | 1 | Mark | 0.98705900 1385770566 | | 2 | George | 0.99367300 1385770566 | +----+--------+-----------------------+
问题是执行的第一个查询应该具有id
值1,并且执行的第二个查询应该具有id
值2.为了解决这个问题,我添加了一个随机睡眠(上面已经注释掉)并且它纠正了问题.我正在使用交易,我认为这将纠正这个问题.我错过了一些明显的东西吗?
对于那些好奇的人,这是我的表格设置:
CREATE TABLE `testing` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `timestamp` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
PHP 5.3.3
MySQL 5.1.69
MySQL不会将一个会话的最后一个插入ID返回给另一个会话.
http://dev.mysql.com/doc/refman/5.6/en/information-functions.html#function_last-insert-id说:
生成的ID在每个连接的基础上在服务器中维护.这意味着函数返回给定客户端的值是为该客户端影响AUTO_INCREMENT列的最新语句生成的第一个AUTO_INCREMENT值.此值不受其他客户端的影响,即使它们生成自己的AUTO_INCREMENT值.此行为可确保每个客户端都可以检索自己的ID,而无需关心其他客户端的活动,也无需锁定或事务.
你的意见:
这从一开始就是MySQL的行为.如果它容易受到竞争条件的影响,那么返回最后一个插入ID将是无用的,即如果其他会话中的插入可能污染您的会话.
一种可能性是您正在使用持久连接,因为旧版本的PHP存在一个错误,即可以将连接提供给新的PHP请求,并允许从先前的PHP请求访问会话范围的状态.换句话说,锁和事务以及临时表和用户变量以及最后一个插入ID之类的东西可以存活到后续的PHP请求中.这些问题应该在PHP 5.3中使用mysqlnd驱动程序解决; 持久连接应该"重置"到初始状态.
另一个可能的解释是它确实正常运行,你的观察中你错了.所以我建议仔细而有条不紊地进行测试.
更新:根据你的回答,这个问题与MySQL或PDO或lastInsertId无关.这听起来像你没有看到在你的PHP代码的输出差异可言,你看到在Chrome浏览器开发工具的网络性能统计数据意外数字.