代码之家  ›  专栏  ›  技术社区  ›  rg88

优化PHP页面:MySQL瓶颈

  •  2
  • rg88  · 技术社区  · 15 年前

    我有一个页面需要37秒才能加载。在加载时,它将MySQL的CPU使用率挂在屋顶上。我没有为这个页面编写代码,它相当复杂,所以瓶颈的原因对我来说并不明显。

    我分析了它(使用kcachegrind),发现页面上的大部分时间都花在了MySQL查询上(90%的时间都花在了25个不同的MySQL_查询调用上)。

    SELECT * FROM tbl_news WHERE news_id
     IN (select news_id from
     tbl_tag_relations WHERE tag_id = 20)
    

    每一个查询大约需要0.8秒才能完成,为了更好的度量,会有一些更长的延迟。。。因此,需要37秒才能完全加载页面。

    我的问题是,是不是用嵌套的select格式化查询的方式导致了问题?或者它可能是百万其他事物中的任何一个?任何关于如何解决这一缓慢问题的建议都将不胜感激。

    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
    
    5 回复  |  直到 15 年前
        1
  •  5
  •   Community Navdeep Singh    7 年前

    我在一篇文章中谈到了这一点 Database Development Mistakes Made by AppDevelopers

    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
    

    顺便说一句,我使用的上述语法是您在进行连接时应该更喜欢的语法。这比把它们放在盒子里更清楚 WHERE 子句(正如其他人所建议的)和上面的语句可以以ANSISQL的方式使用左外部联接完成某些事情,而条件不能这样做。

    • tbl_新闻(新闻id)
    • tbl_标签关系(标签id)

    查询几乎会在瞬间执行。

    最后,不要使用*选择所有需要的列。明确地命名它们。以后添加列时,麻烦会少一些。

        2
  •  3
  •   Andy Moore    15 年前

    SQL查询本身肯定是您的瓶颈。查询中有一个子查询,它是代码的in(…)部分。这实际上是同时运行两个查询。通过连接(类似于上面d03boy提到的)或更有针对性的SQL查询,您可能会将SQL时间减少一半(或更多!)。例如:

    SELECT * 
    FROM tbl_news, tbl_tag_relations 
    WHERE tbl_tag_relations.tag_id = 20 AND
    tbl_news.news_id = tbl_tag_relations.news_id 
    

    SELECT news_title, news_body 
    ... 
    LIMIT 5;
    

    您还需要查看数据库模式本身。确保为所有常用列编制索引,以便查询运行得更快。在这种情况下,您可能需要检查您的news\u id和tag\u id字段。

    最后,您将想看看PHP代码,看看是否可以生成一个包含所有内容的SQL查询,而不是迭代几个单独的查询。如果您发布更多代码,我们可以提供帮助,这可能是为您发布的问题节省的最大时间。:)

        3
  •  2
  •   Joe Phillips    15 年前

    如果我理解正确,这只是列出一组特定标签的新闻故事。

    1. SELECT *

    2. 其次,这可能是
      从而降低了系统的间接成本
      多个查询。好像是这样 获取相当琐碎的数据是否如此 它可以在一个小时内取回 一个电话而不是20个。

    3. IN 可能是使用 JOIN 用一个 WHERE 条件相反。当使用 在里面 基本上会有很多 OR 声明。
    4. 你的 tbl_tag_relations 应该有一个索引 tag_id
        4
  •  1
  •   cgp    15 年前
    select * 
     from tbl_news, tbl_tag_relations 
     where 
          tbl_tag_relations.tag_id = 20 and 
          tbl_news.news_id = tbl_tag_relations.news_id 
     limit 20
    

    我认为这会给出同样的结果,但我不是100%确定。有时简单地限制结果会有所帮助。

        5
  •  1
  •   Ryan Bair    15 年前

    例如,原始查询将为tbl_新闻表中的每个符合条件的行返回1行,但此查询:

    SELECT news_id, name, blah
    FROM tbl_news n
    JOIN tbl_tag_relations r ON r.news_id = n.news_id
    WHERE r.tag_id IN (20,21,22)
    

    这也不算太糟,但大多数其他数据库(PostgreSQL、Firebird、Microsoft、Oracle、DB2等)都会将原始查询作为有效的半联接来处理。就我个人而言,我发现子查询语法更易于阅读和编写,特别是对于较大的查询。