【问题标题】:Lots of queries on one page that take too long to execute... Can't seem to optimize them一页上有很多查询需要很长时间才能执行......似乎无法优化它们
【发布时间】:2013-07-27 18:53:51
【问题描述】:

我有一个博客,通过在表 (views) 中插入访问者的 IP、帖子 ID(我在这些字段上获得主键)和时间戳,我可以跟踪谁查看了哪些帖子以及何时查看。

然后使用此表显示我的每个类别的前 5 个帖子(其中有 4 个)在最后一天/一周/一个月/一年和所有时间。所以,总共执行了 20 个查询,每个查询都需要 0.2 到 0.7 秒之间……我的页面加载需要 7 秒多一点,这太糟糕了。

这里有一些关于我的数据库结构的有用信息:

+---------------------+        +----------------------+
|   posts (82 rows)   |        |   views (50k rows)   |
+=====================+        +======================+
|    id (primary)     |        |     ip (primary)     |
+---------------------+        +----------------------+
|        type         |        | article_id (primary) |
+---------------------+        +----------------------+
|     thumbnail       |        |     date (index)     |
+---------------------+        +----------------------+
|    title (index)    |       
+---------------------+
|         url         |
+---------------------+
| description (index) |
+---------------------+
|       content       | 
+---------------------+
|        date         |
+---------------------+
|       lastmod       |
+---------------------+
|       sources       |
+---------------------+
|        tags         |
+---------------------+
|      published      |
+---------------------+
|         ...         |
+---------------------+

... 代表我的帖子英文版的附加字段(url_entitle_endescription_entags_encontent_en)。

这是我的一个巨大的查询(它们都基本相同):

SELECT p.title, p.id, p.url, tmp.cnt AS views
FROM posts AS p 
LEFT JOIN (SELECT COUNT(*) AS cnt, article_id -- 0.34s
           FROM views
           WHERE article_id IN (SELECT id
                                FROM posts
                                WHERE id <> 12 AND type = 'Tutoriel') AND 
                 date BETWEEN 01-01-2013 AND NOW() -- the 01-01-2013 is normally a variable but for testing purposes I've replaced it with a fixed date here
           GROUP BY article_id
           ORDER BY cnt DESC LIMIT 5) AS tmp 
       ON p.id = tmp.article_id
WHERE p.id IN (SELECT article_id
               FROM (SELECT COUNT(*) AS cnt, article_id -- 0.34s
                     FROM views
                     WHERE article_id IN (SELECT id
                                          FROM posts
                                          WHERE id <> 12 AND type = 'Tutoriel')
                       AND date BETWEEN 01-01-2013 AND NOW()
                     GROUP BY article_id
                     ORDER BY cnt DESC LIMIT 5) AS tmp2 
              )
ORDER BY views DESC

我发现BETWEEN 子句大部分时间都在使用,因为我对所有帖子的所有时间统计信息都进行了完全相同的查询(因此,不依赖于类别或日期)并且只需要0.03 秒执行。

我已经以所有可能的方式查看了这个查询,但找不到更简单、更优化的方式来编写它......但是,我觉得必须有一种方式。也许我只是在这里遗漏了一些明显的东西。

让我烦恼的一件事是我的重复子查询。我没有找到任何其他方法来获取我的帖子数据和相关视图的数量。

我的想法可能是当用户单击该时间段的选项卡(这是一个选项卡式视图)时,为每个时间段执行 AJAX 请求。但是,这并不能真正解决问题,只是感觉像是一种肮脏的解决方法。

我可以通过以下方式之一对我的posts 表进行分区:

  • 一张表用于法语版,另一张用于英文版
  • 一张表用于常用字段(titledescriptionurl),另一张用于其余字段
  • 以上组合

如果我没记错的话,这可能会加快一点。

有人可以给我一些建议吗?顺便说一句,谢谢你一直陪我到这里:)

【问题讨论】:

  • 您没有告诉我们有关索引或数据量的任何信息。由于这是一个非常复杂的问题,您可能需要创建一个SQL Fiddle
  • 抱歉,马上编辑。
  • 对,忘记在我的查询中编辑名称。这些确实是同一张桌子。刚刚编辑。
  • 添加索引以键入会有所帮助。

标签: mysql sql performance optimization subquery


【解决方案1】:

不确定是否会有所帮助,但如果 BETWEEN 需要很长时间,也许可以将其转换为其他条件?

date BETWEEN 01-01-2013 AND NOW()

date > 01-01-2013

所以它不必比较两个日期,它总是在 01-01-2013 和 NOW 之间

【讨论】:

  • 是的...我会试试看是否有帮助。
  • 加快了我的查询速度。从 0.60 秒变为 0.03 秒。不过,我的查询仍然很难看,所以我要试试 Gordon Linoff 上面所说的内容。
【解决方案2】:

旧版本的 MySQL 特别不擅长使用子查询优化 in。尝试改用join

SELECT p.title, p.id, p.url, tmp.cnt AS views
FROM posts AS p 
LEFT JOIN (SELECT COUNT(*) AS cnt, article_id -- 0.34s
           FROM views
           WHERE article_id IN (SELECT id
                                FROM posts
                                WHERE id <> 12 AND type = 'Tutoriel') AND 
                 date BETWEEN 01-01-2013 AND NOW() -- the 01-01-2013 is normally a variable but for testing purposes I've replaced it with a fixed date here
           GROUP BY article_id
           ORDER BY cnt DESC LIMIT 5) AS tmp 
       ON p.id = tmp.article_id join
          (SELECT COUNT(*) AS cnt, article_id -- 0.34s
           FROM views v join
                (SELECT id
                 FROM posts p
                 WHERE p.id <> 12 AND p.type = 'Tutoriel'
                ) p
                on v.article_id = p.id
            WHERE v.date BETWEEN 01-01-2013 AND NOW()
            GROUP BY v.article_id
            ORDER BY cnt DESC
            LIMIT 5
           ) a
       on p.id = a.article_id
ORDER BY views DESC

编辑:

如果我对查询的理解正确,您只需将您的 left outer join 更改为 join 并完全消除 where 子句:

SELECT p.title, p.id, p.url, tmp.cnt AS views
FROM posts Ap JOIN
     (SELECT COUNT(*) AS cnt, article_id -- 0.34s
      FROM views
      WHERE article_id IN (SELECT id
                           FROM posts
                           WHERE id <> 12 AND type = 'Tutoriel') AND 
            date BETWEEN 01-01-2013 AND NOW() -- the 01-01-2013 is normally a variable but for testing purposes I've replaced it with a fixed date here
     GROUP BY article_id
     ORDER BY cnt DESC
     LIMIT 5
    ) tmp 
    ON p.id = tmp.article_id;

然后将子查询中的in改为join:

SELECT p.title, p.id, p.url, tmp.cnt AS views
FROM posts Ap JOIN
     (SELECT COUNT(*) AS cnt, article_id -- 0.34s
      FROM views v join
           (SELECT distinct p.id  -- distinct may not be necessary
            FROM posts p
            WHERE p.id <> 12 AND p.type = 'Tutoriel'
           ) p
           on v.rticle_id = p.id
      WHERE date BETWEEN 01-01-2013 AND NOW() -- the 01-01-2013 is normally a variable but for testing purposes I've replaced it with a fixed date here
     GROUP BY article_id
     ORDER BY cnt DESC
     LIMIT 5
    ) tmp 
    ON p.id = tmp.article_id;

【讨论】:

  • 这里不错。将其降至 0.02 秒。结合 itachi42 所说的,它下降到了 0.0004 秒。非常感谢!
猜你喜欢
  • 2018-07-06
  • 1970-01-01
  • 2016-04-22
  • 2021-12-25
  • 2016-06-17
  • 1970-01-01
  • 2014-01-15
  • 2023-03-12
  • 2012-12-03
相关资源
最近更新 更多