【问题标题】:MySQL Query on Only Recent Items for Big Table?MySQL 仅查询大表的最近项目?
【发布时间】:2014-08-26 21:33:40
【问题描述】:

我们在我们的论坛中实施了“感谢”系统。我们还有一个“查看您最近的感谢”页面,随着“感谢”表增长到数百万,该页面现在变得越来越慢。

这是“Thanks”表的结构:

Field    Type        Null   Key Default Extra 
id       int(10)     NO     PRI NULL    auto_increment 
userid   int(10)     NO     MUL NULL 
username varchar(50) NO         NULL 
date     int(10)     NO         NULL 
postid   int(10)     NO     MUL NULL 

在“最近的感谢”页面上,为当前用户 ($uid) 运行以下查询:

  SELECT post_thanks.postid, post_thanks.date, post_thanks.username, post_thanks.userid, thread.title 
    FROM " . TABLE_PREFIX . "post_thanks AS post_thanks
         LEFT JOIN " . TABLE_PREFIX . "post AS post
                ON post_thanks.postid = post.postid
         LEFT JOIN " . TABLE_PREFIX . "thread AS thread
                ON thread.threadid = post.threadid      
   WHERE post.userid = '$uid'
   ORDER BY post_thanks.id DESC
   LIMIT 20

这会导致该用户的 20 条最新帖子感谢您。现在 post_thanks 表的长度超过一百万行,这个查询需要一秒多的时间才能完成。有时它来自缓存的速度更快,但我一直在努力优化它,因为用户通常只在收到新通知时才查看他们的“谢谢”(即缓存会发生变化)。

我的第一个问题是这个查询使用 Filesort 而不是 Index。给定表结构,ORDER BY post_thanks.id 不应该使用索引吗?

我的第二个问题是我不需要查询 500 万行来获得最近的感谢。整个论坛每周通常有 5,000 次感谢。如果用户一周不查看论坛,那么他们最近的感谢可能已经消失了,这没关系。所以我修改了这样的查询(唯一改变的行是#2)

  SELECT post_thanks.postid, post_thanks.date, post_thanks.username, post_thanks.userid, thread.title 
  FROM (SELECT * FROM " . TABLE_PREFIX . "post_thanks ORDER BY id DESC LIMIT 5000) AS post_thanks
  LEFT JOIN " . TABLE_PREFIX . "post AS post
  ON post_thanks.postid = post.postid
  LEFT JOIN " . TABLE_PREFIX . "thread AS thread
  ON thread.threadid = post.threadid    
  WHERE post.userid = '$uid' 
  ORDER BY post_thanks.id DESC
  LIMIT 20

即使在禁用缓存的情况下,此查询也始终以不到 0.001 秒的速度运行。

这是处理性能问题的好方法吗?或者有没有更好的方法来加速这个查询而不忽略旧数据?我仍然对为什么不以任何方式使用 Index 感到困惑。

【问题讨论】:

  • 你的架构是什么样的?您是否正确创建了键和索引?
  • 他的查询计划是什么样的?您是否看过使用 EXPLAIN(请参阅此处的参考:dev.mysql.com/doc/refman/5.0/en/using-explain.html
  • 顺便说一下,您的 5000 条限制适用于最近的 5000 条帖子......不是特定用户的最新历史记录。
  • @Dan,我运行了“描述 post_thanks”。还有什么我应该跑来和你分享架构的吗?

标签: mysql sql performance


【解决方案1】:

尝试使用inner join 而不是left join 运行查询:

SELECT post_thanks.postid, post_thanks.date, post_thanks.username, post_thanks.userid, thread.title 
FROM " . TABLE_PREFIX . "post_thanks post_thanks INNER JOIN
     " . TABLE_PREFIX . "post AS post
     ON post_thanks.postid = post.postid INNER JOIN
     " . TABLE_PREFIX . "thread AS thread
     ON thread.threadid = post.threadid      
WHERE post.userid = '$uid'
ORDER BY post_thanks.id DESC
LIMIT 20;

接下来,我注意到where 条件是post.userid = '$uid'。这真的是还是也是:post_thanks.userid = '$uid'?

我建议使用内部联接,带有where post_thanks.userid = '$uid' 和索引:post_thanks(userid, id)

【讨论】:

  • post_thanks.userid 是感谢的人的用户 ID,而 post.userid 是接受感谢的人的用户 ID。我会尝试内部连接。我怎样才能像你建议的那样指定索引?我以为 MySQL 会自动决定使用哪个索引?
  • @PFBilling 。 . .也许您可以更改thanks 表以在其中包含两个用户ID。这个小改动将使查询更容易优化。
  • 我会试一试。我想我只是对为什么限制 3000 可以节省这么多时间感到困惑。原始查询不应该只是排序并获取userid = $uid的前20个,然后停止排序吗?考虑到时间差异,它似乎是对所有 500 万行进行排序,然后只抓取前 20 行。
  • @PFBilling 。 . .所有符合条件的数据都会被排序。 then 返回前 20 个结果。我怀疑它正在对 500 万行进行排序。它可能会得到所有的感谢(因为left join),按用户过滤这些结果,对结果进行排序,然后选择前 20 个。您可以将explain 放在查询之前以了解它在做什么.
  • 您在第二条评论中的解决方案让这如闪电般迅速。刚刚在 post_thanks 表中为 received_userid 添加了一个新列,对现有条目运行了快速数据更新,现在不需要对 post 表进行更多的大规模连接。谢谢!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-13
  • 2014-01-11
  • 2020-03-05
  • 2015-05-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多