我通过命令行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 上使用
有更多信息