为什么在PHP和MySQL中使用PDO的某些类型的预处理查询速度较慢?
使用带有prepare()/execute()的PDO在(..)中使用Id的为什么在PHP和MySQL中使用PDO的某些类型的预处理查询速度较慢?,php,mysql,performance,pdo,Php,Mysql,Performance,Pdo,使用带有prepare()/execute()的PDO在(..)中使用Id的SELECT*FROM table WHERE Id查询超过10000个键时,与使用带有prepare语句的mysqli或不使用prepared语句的PDO执行相同查询相比,性能降低约10倍 更奇怪的细节: 更典型的SELECT语句没有(..)子句中的WHERE Id,即使有100K+行,也可以正常执行从Id快速的表格中选择* 性能下降发生在prepare()/execute()完成后-它完全在PDOStatement
SELECT*FROM table WHERE Id查询超过10000个键时,与使用带有prepare语句的mysqli或不使用prepared语句的PDO执行相同查询相比,性能降低约10倍
更奇怪的细节:
- 更典型的SELECT语句没有(..)
子句中的WHERE Id,即使有100K+行,也可以正常执行<代码>从Id
快速的表格中选择*
PDOStatement::fetch()
或PDOStatement::fetchAll()
中。在所有情况下,MySQL查询执行时间都很短——这不是MySQL优化的情况// $imageIds is an array with 10K keys
$keyCount = count($imageIds);
$keys = implode(', ', array_fill(0, $keyCount, '?'));
$query = "SELECT * FROM images WHERE ImageID IN ({$keys})";
$stmt = $dbh->prepare($query);
$stmt->execute($imageIds);
// until now, it's been fast. fetch() is the slow part
while ($row = $stmt->fetch()) {
$rows[] = $row;
}
确保您告诉PDO该值是整数而不是字符串;如果PDO将其作为字符串,那么MySQL将不得不对值进行类型转换以进行比较。这取决于它是如何进行的,它可能会导致MySQL避免使用索引,从而导致严重的速度减慢
我不完全确定这里的行为,但几年前我在Postgres上遇到过这个问题…我没有任何PDO经验,因此无法帮助解决这个问题,但这种方法非常有效,尽管有些地方有点难看;) PHP MySQL
示例代码中有一些主要错误。所以更准确地说
// $imageIds is an array with 10K keys
$keyCount = count($imageIds);
$keys = implode(', ', array_fill(0, $keyCount, '?'));
$query = "SELECT * FROM images WHERE ImageID IN ({$keys})";
到目前为止,上述代码将提供如下内容……
SELECT * FROM images WHERE ImageID IN (?, ?, ?, ?, ?, ?,...?, ?, ?, ?)
没有用于绑定的循环。。。应该有一个小循环,您可以在其中绑定传递给MySQL的所有参数。您可以从prepare
转到execute
,前提是您需要的是正确
$stmt = $dbh->prepare($query);
$stmt->execute($imageIds);
// until now, it's been fast. fetch() is the slow part
while ($row = $stmt->fetch()) {
$rows[] = $row;
}
现在我有一个关于这部分问题的简单逻辑问题
使用时,从(..)中Id所在的表中选择*
查询更多信息
比使用PDO和prepare()/execute()的10000个键,性能
与使用mysqli和
已准备好的语句或未使用已准备好的语句的PDO
如果重新编写相同的查询,这样您就不需要传递10000个键作为参数,这不是更好吗
PDO
和MySQLi
在计时方面没有重大差异。写得不好的查询也可以。非常复杂的存储过程如果没有得到很好的优化,有时可能会变得很慢
检查另一个查询是否可以获取所需的结果。例如
创建一个名为test
create table `test` (
`id` int(10) not null,
`desc` varchar(255)
);
insert into `test` (`id`,`desc`) values (1,'a'),(10,'a1'),(11,'a2'),(12,'a3'),(13,'a4'),(14,'a5'),(15,'a6'),(2,'ab'),(20,'ab1'),(21,'ab2'),(22,'ab3'),(23,'ab4'),(24,'ab5'),(25,'ab6');
运行那些简单的查询
select * from `test` where `id` rlike '^1$';
select * from `test` where `id` rlike '^1+';
select * from `test` where `id`=1;
select * from `test` where `id` rlike '^1.$';
select * from `test` where `id` rlike '.2$';
select * from `test` where `id` rlike '^2$';
select * from `test` where `id` rlike '.(2|3)'; // Slower
select * from `test` where `id` IN (12,13,22,23); // Faster
select * from `test` where `id` IN ('12,13,22,23'); // Wrong result
select * from `test` where `id` IN ('12','13','22','23'); // Slower
在本例中,最后4个查询的结果相同。我认为,大多数情况下,如果你检查它,你会得到与给定标签对应的查询时间。如果这是可复制的,那么你可能需要分析PHP以了解为什么会出现减速。从你的描述来看,这听起来像是一个后处理错误。我怀疑减速是由于处理绑定参数造成的。尝试
->debugDumpParams()
并查找is_param=
值。如果是1
,则PDO将在列表上迭代以查找要更新的绑定变量。可能手动使用->bindValue()
而不是->execute(ARRAY)
预存有帮助。但我怀疑PDO总是在绑定参数列表上循环。不确定is_param=是否对这一点起决定性作用。(而且懒得理解pdo_stmt.c)注意,在mysqli中将参数作为字符串绑定到$stmt->bind_参数(str_repeat('s',count($imageIds)),…$imageIds)代码>不比将它们绑定为整数慢。而且这两种mysqli方法都需要比未准备好的语句多50%的时间。但是PDO准备好的语句要慢50倍(使用10K参数)。所以PDO不能总是将参数绑定为字符串。甚至$dbh->setAttribute(PDO::ATTR\u EMULATE\u PREPARES,true)代码>不会改变任何东西。PDO发生了一些非常奇怪的事情。你知道这是什么原因吗?我遇到了完全相同的问题。相关的bug报告:-@mario对这个问题很有帮助。获取时间与返回行数和绑定参数数成正比。对于这类查询,应该是线性的问题变成了O(n²)。这意味着:参数增加100倍=>10000倍。嘿,f00,谢谢!唉,我已经有了MySQL、MySQLi、PDO的工作函数,没有准备好的语句,等等——我想知道为什么这个特定的用例如此混乱。出于各种原因,我更喜欢PDO w/prepared语句。无需担心:)只是出于兴趣,您得到了什么样的运行时???@donmacaskill您的源代码没有表明在PREPARE和EXECUTE之后使用了接近免费的资源。见乔恩·布莱克的《源头的终结》。+1,因为这可能是缓慢的原因。强制转换将简化索引的使用。我以前在mysql+PDO准备的语句中遇到过完全相同的问题。上次我查看代码时,PDO mysql驱动程序总是以字符串形式传递参数。大多数数据类型参数都被忽略(除了PDO::PARAM_LOB
和PDO::PARAM_NULL
)。其他品牌的RDBMS的其他PDO驱动程序也需要它们。请注意,mysqli
将参数作为字符串进行良好绑定。一个包含字符串列表的无准备语句也很快。我不理解你的回答
$stmt = $dbh->prepare($query);
$stmt->execute($imageIds);
// until now, it's been fast. fetch() is the slow part
while ($row = $stmt->fetch()) {
$rows[] = $row;
}
create table `test` (
`id` int(10) not null,
`desc` varchar(255)
);
insert into `test` (`id`,`desc`) values (1,'a'),(10,'a1'),(11,'a2'),(12,'a3'),(13,'a4'),(14,'a5'),(15,'a6'),(2,'ab'),(20,'ab1'),(21,'ab2'),(22,'ab3'),(23,'ab4'),(24,'ab5'),(25,'ab6');
select * from `test` where `id` rlike '^1$';
select * from `test` where `id` rlike '^1+';
select * from `test` where `id`=1;
select * from `test` where `id` rlike '^1.$';
select * from `test` where `id` rlike '.2$';
select * from `test` where `id` rlike '^2$';
select * from `test` where `id` rlike '.(2|3)'; // Slower
select * from `test` where `id` IN (12,13,22,23); // Faster
select * from `test` where `id` IN ('12,13,22,23'); // Wrong result
select * from `test` where `id` IN ('12','13','22','23'); // Slower