Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么在PHP和MySQL中使用PDO的某些类型的预处理查询速度较慢?_Php_Mysql_Performance_Pdo - Fatal编程技术网

为什么在PHP和MySQL中使用PDO的某些类型的预处理查询速度较慢?

为什么在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

使用带有prepare()/execute()的PDO在(..)中使用Id的
SELECT*FROM table WHERE Id查询超过10000个键时,与使用带有prepare语句的mysqli或不使用prepared语句的PDO执行相同查询相比,性能降低约10倍

更奇怪的细节:

  • 更典型的SELECT语句没有(..)
子句中的
WHERE Id,即使有100K+行,也可以正常执行<代码>从Id
快速的表格中选择*

  • 性能下降发生在prepare()/execute()完成后-它完全在
    PDOStatement::fetch()
    PDOStatement::fetchAll()
    中。在所有情况下,MySQL查询执行时间都很短——这不是MySQL优化的情况

  • 将10K查询拆分为10个具有1K密钥的查询是很有效的

  • 使用mysql、带预处理语句的mysqli或不带预处理语句的PDO都是有效的

  • 在下面的示例中,PDO w/prepared大约需要6秒,而其他PDO大约需要0.5秒

  • 你拥有的钥匙越多,情况就会以非线性的方式变得更糟。试试100K键

  • 示例代码:

    // $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