PHP运行查询的时间是MySQL客户端的90倍


PHP takes 90x longer to run query than MySQL client

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

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

当我通过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客户端都能立即运行它,我不认为查询有错,但以下是kicks的解释:

+----+-------------+-------+--------+-------------------------+------------+---------+-----------------------------------+------+-------------+
| 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秒?

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

对这个问题给出了一个非常迟来的更新:

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

在这种(令人困惑的)情况下,解决方案是使用索引暗示。请参阅我的示例中修改后的查询中的"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;

希望这能帮助到别人!

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

PDO使用资源来操纵行结果。用解释语言(PHP)处理它,你会得到比MySQL返回结果需要更长时间处理的脚本。

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

要了解有关更快的PHP查询的更多信息,请参阅:PHP:What';查询MySQL最快的方法是什么?因为PDO的非常慢

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

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

试着把一些unicode字符放在那里,它可能会开始返回糟糕的结果。

确保连接、表和列上的字符集匹配,以获得最佳结果。如果无法完成,则连接是最重要的

UTF-8与Latin1 mysql,索引未在UTF-8 上使用

有更多信息