我有一个页面需要 37 秒才能加载。当它加载时,它会通过屋顶挂住 MySQL 的 CPU 使用率。我没有为这个页面编写代码,它相当复杂,所以瓶颈的原因对我来说并不明显。
我分析了它(使用 kcachegrind)并发现页面上的大部分时间花在了 MySQL 查询上(90% 的时间花在了 25 个不同的 mysql_query 调用上)。
查询采用以下形式,其中 tag_id 在 25 次不同的调用中的每一次发生变化:
SELECT * FROM tbl_news WHERE news_id IN (select news_id from tbl_tag_relations WHERE tag_id = 20)
每个查询大约需要 0.8 秒才能完成,为了更好地衡量,还会有一些更长的延迟……因此需要 37 秒才能完全加载页面。
我的问题是,导致问题的是使用嵌套选择格式化查询的方式吗?或者它可能是一百万种其他事物中的任何一种?对于如何解决这种缓慢的任何建议,我们表示赞赏。
在查询上运行 EXPLAIN 给我这个(但我不清楚这些结果的影响......主键上的 NULL 看起来很糟糕,是吗?返回的结果数量对我来说似乎很高以及最后只返回少量结果):
1 PRIMARY tbl_news ALL NULL NULL NULL NULL 1318 Using where 2 DEPENDENT SUBQUERY tbl_tag_relations ref FK_tbl_tag_tags_1 FK_tbl_tag_tags_1 4 const 179 Using where
最佳答案
我在 Database Development Mistakes Made by AppDevelopers 中解决了这一点.基本上,赞成加入聚合。 IN 本身不是聚合,但适用相同的原则。一个好的优化将使这两个查询在性能上相当:
SELECT * FROM tbl_news WHERE news_id
IN (select news_id from
tbl_tag_relations WHERE tag_id = 20)
和
SELECT tn.*
FROM tbl_news tn
JOIN tbl_tag_relations ttr ON ttr.news_id = tn.news_id
WHERE ttr.tag_id = 20
我相信 Oracle 和 SQL Server 都可以,但 MySQL 不会。第二个版本基本上是瞬时的。对于数十万行,我在我的机器上进行了测试,并通过添加适当的索引获得了亚秒级性能的第一个版本。带有索引的连接版本基本上是瞬时的,但即使没有索引也可以执行。
顺便说一句,我使用的上述语法是您在进行连接时应该更喜欢的语法。它比将它们放在 WHERE
子句中(正如其他人所建议的那样)更清楚,并且上面可以用 ANSI SQL 方式使用 WHERE 条件不能做的左外连接来做某些事情。
所以我会在以下内容上添加索引:
- tbl_news (news_id)
- tbl_tag_relations (news_id)
- tbl_tag_relations (tag_id)
查询几乎会立即执行。
最后,不要使用 * 来选择您想要的所有列。明确命名它们。稍后添加列时,您会遇到更少的麻烦。
关于php - 优化 PHP 页面 : MySQL bottleneck,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/789957/