【问题标题】:PHP takes 90x longer to run query than MySQL clientPHP 运行查询的时间比 MySQL 客户端长 90 倍
【发布时间】:2012-09-02 10:05:39
【问题描述】:

我正在通过命令行 PHP 脚本运行 MySQL 查询(使用 mysqlnd 驱动程序上的 PDO 准备查询)。这是一个带有单个左连接的简单查询,每行返回 100 行和 7 个小列。

当我在 MySQL CLI 中运行此查询时(在运行相关 PHP 脚本的同一台机器上),它需要 0.10 秒——即使抛出了 SQL_NO_CACHE 标志。

当我通过 PDO 运行这个准备好的查询时,它需要 9 多秒。这只是 execute() -- 不包括 fetch 调用所花费的时间。

我的查询示例:

SELECT HEX(al.uuid) hexUUID, al.created_on,
    IFNULL(al.state, 'ON') actionType, pp.publishers_id publisher_id,
    pp.products_id product_id, al.action_id, al.last_updated
FROM ActionAPI.actionLists al
LEFT JOIN ActionAPI.publishers_products pp
    ON al.publisher_product_id = pp.id
WHERE (al.test IS NULL OR al.test = 0)
    AND (al.created_on >= :since OR al.last_updated >= :since)
ORDER BY created_on ASC
LIMIT :skip, 100;

我不认为查询有问题,考虑到我尝试过的每个本地 MySQL 客户端都几乎立即运行它,但这里有一些解释:

+----+-------------+-------+--------+-------------------------+------------+---------+-----------------------------------+------+-------------+
| id | select_type | table | type   | possible_keys           | key        | key_len | ref                               | rows | Extra       |
+----+-------------+-------+--------+-------------------------+------------+---------+-----------------------------------+------+-------------+
|  1 | SIMPLE      | al    | index  | created_on,last_updated | created_on | 8       | NULL                              |  100 | Using where |
|  1 | SIMPLE      | pp    | eq_ref | PRIMARY                 | PRIMARY    | 4       | ActionAPI.al.publisher_product_id |    1 |             |
+----+-------------+-------+--------+-------------------------+------------+---------+-----------------------------------+------+-------------+
2 rows in set (0.00 sec)

PDO 到底在做什么,需要 8.9 秒?

编辑: 正如 cmets 中所述,我也编写了一个 mysql_query 版本,它的性能同样很差。然而,删除部分 WHERE 子句使其运行速度与 MySQL 客户端一样快。继续阅读,了解令人难以置信的细节。

【问题讨论】:

  • 只是为了好玩,你介意用mysql_query() 运行一次,看看它是否是PDO 的东西吗?
  • 再说一遍,“只是为了好玩。”就是看看是不是PDO的事情。
  • 那么我们就知道这不是 PDO 的事情...... 艰难的人群。
  • 不,你知道它和 mysql 绑定一样慢,我们知道它严重未优化并且不再维护。使用mysql可能会导致更多的混乱,如果你编程的时间足够长,你就会知道如何隔离问题。
  • 您能否尝试逐一删除部分查询(WHERE 子句、ORDER、LIMIT、HEX/ISNULL 结果字段之一),看看是否有区别?

标签: php mysql pdo


【解决方案1】:

在这个问题上给出一个很晚的更新:

我还没有找到原因,但事实证明 PHP 中的 EXPLAIN 与 CLI 中的不同。我不确定连接的任何方面是否会导致 MySQL 选择为索引使用不同的字段,因为据我所知,这些事情不应该相关;但可惜的是,PHP 的 EXPLAIN 显示没有使用正确的索引,而 CLI 却使用了。

在这种(令人困惑的)情况下,解决方案是使用index hinting。请参阅我的示例中修改后的查询中的“FROM”行:

SELECT HEX(al.uuid) hexUUID, al.created_on,
    IFNULL(al.state, 'ON') actionType, pp.publishers_id publisher_id,
    pp.products_id product_id, al.action_id, al.last_updated
FROM ActionAPI.actionLists al USE INDEX (created_on)
LEFT JOIN ActionAPI.publishers_products pp
    ON al.publisher_product_id = pp.id
WHERE (al.test IS NULL OR al.test = 0)
    AND (al.created_on >= :since OR al.last_updated >= :since)
ORDER BY created_on ASC
LIMIT :skip, 100;

希望这对某人有所帮助!

【讨论】:

  • 猜测是您的连接字符类型设置与表或列字符类型不匹配。 Mysql 有时会忽略索引,因为字符集的差异,它认为它不能使用索引。
  • 如何使用PHP的EXPLAIN?我在 PDO 手册中找不到参考资料。您是否只是在查询的开头手动添加“解释”,还是在某处我找不到 PDO::explain 函数?
  • @Benubird:是的,我只是在 SELECT 前面扔了一个“EXPLAIN”,然后把结果转储了。
【解决方案2】:

我遇到了同样的问题。从 cli 和 PHP 启动时,相同的查询表现不同。在 cli 中解释提到正确的索引用法,在 PHP 中什么都没有。正如我所发现的,问题在于类型转换,在我的情况下是日期时间。在我专门为比较值转换类型之后,例如。 where datetime_column > cast('2014-01-12 12:30:01' as datetime) 一切正常。

【讨论】:

    【解决方案3】:

    PDO 使用资源来操作行结果。将其与解释语言 (PHP) 相提并论,您将拥有比 MySQL 返回结果所需的脚本更长的处理时间。

    注意:使用 mysql_select_db() 或 mysqli_select_db() 比 PDO 快得多。

    要了解有关更快的 PHP 查询的更多信息,请参阅:PHP: What's the fastest way to query MySQL? Because PDO is painfully slow

    【讨论】:

    • 当然,我希望 PDO 的运行速度比 MySQL 慢。但是我已经用 PHP/MySQL 编程了 ,对于这样一个简单的结果集,查询时间的开销是 90 倍,远远超过解释语言层可以解释的时间.这里一定有更多的事情发生。
    • 好吧,我想这个想法是 mysqli 不会受到伤害,以回应 OP 评论线程中的 @Flavious。
    【解决方案4】:

    当您在命令行上连接时,它很可能使用与 PHP 连接时不同的字符集。

    当您要求它使用索引时,它会并且因为字符集足够接近而不会引起问题(猜测?这取决于您对表和列的设置)

    尝试在那里扔一些 unicode 字符,它可能会开始返回错误的结果。

    确保字符集在连接、表和列上匹配以获得最佳结果。如果他们做不到,连接是最重要的

    UTF-8 vs Latin1 mysql, indexes not used on utf-8

    有更多信息

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-01-28
      • 1970-01-01
      • 2019-07-09
      • 1970-01-01
      • 2016-02-18
      • 1970-01-01
      • 2016-03-12
      相关资源
      最近更新 更多