在 MySQL 中,如何加入两个在 WHERE 条件下都有列的非常大的表?

New*_*ewt 5 mysql optimization join

我正在尝试确定查询具有大量数据的连接的两个表的最佳通用方法,其中每个表在 where 子句中都有一列。想象一个带有两个表的简单模式:

posts
 id (int)
 blog_id (int)
 published_date (datetime)
 title (varchar)
 body (text)

posts_tags 
 post_id (int)
 tag_id (int)
Run Code Online (Sandbox Code Playgroud)

具有以下索引:

posts: [blog_id, published_date]
tags: [tag_id, post_id]
Run Code Online (Sandbox Code Playgroud)

我们想要选择给定博客上标记为“foo”的 10 篇最新帖子。为便于讨论,假设该博客有 1000 万篇帖子,其中 100 万篇被标记为“foo”。查询此数据的最有效方法是什么?

天真的方法是这样做:

 SELECT 
  id, blog_id, published_date, title, body
 FROM 
  posts p
 INNER JOIN
  posts_tags pt 
  ON pt.post_id = p.id
 WHERE
  p.blog_id = 1
  AND pt.tag_id = 1
 ORDER BY
  p.published_date DESC
 LIMIT 10
Run Code Online (Sandbox Code Playgroud)

MySQL 将使用我们的索引,但最终仍会扫描数百万条记录。是否有更有效的方法来检索此数据而无需对架构进行非规范化?

Mar*_*ers 3

最有可能的是,MySQL 将首先使用索引(blog_id, published_date)来扫描所有满足条件的行,blog_id = 1从最新的行开始published_date。为此,只需从正确的位置开始向后扫描索引即可。对于每一行,它必须连接到表posts_tags。此时, 和tag_id都是post_id已知的,因此只需在主索引中查找以查看该行是否存在。10% 的行具有标记,foo因此posts 在找到结果集的前 10 行之前,平均需要检查表中大约 100 行。

如果标签foo很常见,我希望您发布的查询运行得很快。我不认为它会检查数百万行 - 如果不幸的话,可能会检查几百行或几千行。一旦找到 10 个匹配行,它就可以停止而不检查更多行。

另一方面,如果您选择出现次数少于 10 次的标签,速度会很慢,因为它必须扫描该博客中的所有行。

您是否有性能测量结果表明,即使您正在搜索的标签经常出现,查询也特别慢?EXPLAIN您可以发布查询的输出吗?