优化内部连接查询
Optimizing Inner Join Queries
我有这个查询,我想知道我是否可以以某种方式优化它,因为目前它需要很长时间才能执行(比如 4/5 秒)
SELECT *
FROM `posts` ml INNER JOIN
posts_tag_one gt
ON gt.post_id = ml.id AND gt.tag_id = 15 INNER JOIN
posts_tag_two gg
ON gg.post_id = ml.id AND gg.tag_id = 5
WHERE active = '1' AND NOT ml.id = '639474'
ORDER BY ml.id DESC
LIMIT 5
我想说的是数据库有 600k+ 个帖子,posts_tag_one 500 万条记录,posts_tag_two 475k+ 条记录。
我给出的那个例子只有 2 个连接,但在某些情况下我有多达 4 个连接,所以其他表有大约 300k-400k 条记录。
我正在为 posts_tag_one、posts_tag_two 表使用外键和索引,但查询速度仍然很慢。
任何建议都会有所帮助。谢谢!
通过传递属性(如果a=b和b=c,则a=c),你的ML.ID = GT.Post_ID = GG.Post_ID。由于您正在尝试对特定标签进行资格预审,因此我会重写并尝试通过移动到前面的位置并使用更好的索引来优化查询来查看数据的基数是否有帮助。另外,MySQL 有一个很好的关键字“STRAIGHT_JOIN”,它告诉引擎按照我告诉你的顺序查询数据,不要替我想。我已经用过很多次了,看到明显的改善。
SELECT STRAIGHT_JOIN
*
FROM
posts_tag_two gg
INNER JOIN posts_tag_one gt
ON gg.post_id = gt.post_id
AND gt.tag_id = 15
INNER JOIN posts ml
ON gt.post_id = ml.id
AND ml.active = 1
WHERE
gg.tag_id = 5
AND NOT gg.post_id = 639474
ORDER BY
gg.post_id DESC
LIMIT 5
我会确保以下table/多字段索引
table index
Posts_Tag_One ( tag_id, post_id )
Posts_Tag_Two ( tag_id, post_id )
posts ( id, active )
从您为 tag_id = 5 预过滤的 Posts_Tag_Two table 开始,您已经将列表缩减为那些预先合格的 FIRST。不是从所有帖子开始,然后查看哪些符合标签。
第二级连接是到相同 ID 上的 POSTS_TAG_ONE table,但该级别被其 Tag_ID = 15 过滤。
只有这样它才会关心到 POSTS table 以获得活跃。
由于按照ID降序排列,Posts_tag_two table "post_id" 与Posts.id的值相同,索引来自posts_tag_two table 应该 return 记录已经预先排序。
HTH,并且有兴趣了解最终的性能差异。同样,我多次使用 STRAIGHT_JOIN,性能有了显着提高。我通常也不会对所有 table s /所有列执行“Select *”。得到你需要的。
反馈
@eshirvana,在很多情况下,是的,优化器默认执行。但有时,设计师更了解数据的构成。让我们以 POSTS 的场景为例。你有一个装满帖子的盒子的房间。每个盒子包含 10k 条记录。您必须遍历所有 10k 条记录,然后转到下一个框,直到您遍历 400k 条记录……再次举个例子。一旦找到这些,它就会根据特定标签的过滤条件进行连接。这些也是按 ID 排序的,因此您必须进行一对一关联。所以哪个 table 留在主要位置。
现在,按标签索引和 posts_tag table 之一(选择较小的是 #2)。
现在,你有一屋子的盒子,但每个盒子里只有一个标签。如果您有 300 个标签 ID 可用,则您已经剪掉了 x 条记录,只为您提供了您预审资格的小样本。
所以现在,第二个帖子table同样是一屋子的箱子。他们的盒子也按标签分类。所以现在你只需要抓取标签#15 的盒子。
所以现在您有两个非常有限的记录集,JOIN 可以匹配两种情况下都存在的 ID。只有完成后,您才需要转到帖子 table,通过 ID 可以快速直接。但是在索引中具有活动状态,在满足所有条件之前,引擎永远不需要转到任何实际数据页来检索数据。只有这样,它才会从正在 returned.
的 3 个相应 table 中提取记录
听起来 posts_tags
是一个多对多映射 table?它需要两个索引:(post_id, tag_id)
和 (tag_id, post_id)
。其中之一可能应该是 PRIMARY KEY
(拥有 auto_increment id 是一种浪费并且会减慢速度)。另一个应该是INDEX
(不是UNIQUE
)。更多讨论:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
但是,为什么 posts_tag_two
和 posts_tag_one
都有?
除了那些'composite'键之外,还没有单列(post_id)
或(tag_id)
.
如果 tag
只是一个短字符串,请不要对它进行规范化;只需将它放在 table.
如需进一步讨论,请为每个 table 提供 SHOW CREATE TABLE
。还有 EXPLAIN SELECT ...
.
我有这个查询,我想知道我是否可以以某种方式优化它,因为目前它需要很长时间才能执行(比如 4/5 秒)
SELECT *
FROM `posts` ml INNER JOIN
posts_tag_one gt
ON gt.post_id = ml.id AND gt.tag_id = 15 INNER JOIN
posts_tag_two gg
ON gg.post_id = ml.id AND gg.tag_id = 5
WHERE active = '1' AND NOT ml.id = '639474'
ORDER BY ml.id DESC
LIMIT 5
我想说的是数据库有 600k+ 个帖子,posts_tag_one 500 万条记录,posts_tag_two 475k+ 条记录。
我给出的那个例子只有 2 个连接,但在某些情况下我有多达 4 个连接,所以其他表有大约 300k-400k 条记录。
我正在为 posts_tag_one、posts_tag_two 表使用外键和索引,但查询速度仍然很慢。
任何建议都会有所帮助。谢谢!
通过传递属性(如果a=b和b=c,则a=c),你的ML.ID = GT.Post_ID = GG.Post_ID。由于您正在尝试对特定标签进行资格预审,因此我会重写并尝试通过移动到前面的位置并使用更好的索引来优化查询来查看数据的基数是否有帮助。另外,MySQL 有一个很好的关键字“STRAIGHT_JOIN”,它告诉引擎按照我告诉你的顺序查询数据,不要替我想。我已经用过很多次了,看到明显的改善。
SELECT STRAIGHT_JOIN
*
FROM
posts_tag_two gg
INNER JOIN posts_tag_one gt
ON gg.post_id = gt.post_id
AND gt.tag_id = 15
INNER JOIN posts ml
ON gt.post_id = ml.id
AND ml.active = 1
WHERE
gg.tag_id = 5
AND NOT gg.post_id = 639474
ORDER BY
gg.post_id DESC
LIMIT 5
我会确保以下table/多字段索引
table index
Posts_Tag_One ( tag_id, post_id )
Posts_Tag_Two ( tag_id, post_id )
posts ( id, active )
从您为 tag_id = 5 预过滤的 Posts_Tag_Two table 开始,您已经将列表缩减为那些预先合格的 FIRST。不是从所有帖子开始,然后查看哪些符合标签。
第二级连接是到相同 ID 上的 POSTS_TAG_ONE table,但该级别被其 Tag_ID = 15 过滤。
只有这样它才会关心到 POSTS table 以获得活跃。
由于按照ID降序排列,Posts_tag_two table "post_id" 与Posts.id的值相同,索引来自posts_tag_two table 应该 return 记录已经预先排序。
HTH,并且有兴趣了解最终的性能差异。同样,我多次使用 STRAIGHT_JOIN,性能有了显着提高。我通常也不会对所有 table s /所有列执行“Select *”。得到你需要的。
反馈
@eshirvana,在很多情况下,是的,优化器默认执行。但有时,设计师更了解数据的构成。让我们以 POSTS 的场景为例。你有一个装满帖子的盒子的房间。每个盒子包含 10k 条记录。您必须遍历所有 10k 条记录,然后转到下一个框,直到您遍历 400k 条记录……再次举个例子。一旦找到这些,它就会根据特定标签的过滤条件进行连接。这些也是按 ID 排序的,因此您必须进行一对一关联。所以哪个 table 留在主要位置。
现在,按标签索引和 posts_tag table 之一(选择较小的是 #2)。 现在,你有一屋子的盒子,但每个盒子里只有一个标签。如果您有 300 个标签 ID 可用,则您已经剪掉了 x 条记录,只为您提供了您预审资格的小样本。
所以现在,第二个帖子table同样是一屋子的箱子。他们的盒子也按标签分类。所以现在你只需要抓取标签#15 的盒子。
所以现在您有两个非常有限的记录集,JOIN 可以匹配两种情况下都存在的 ID。只有完成后,您才需要转到帖子 table,通过 ID 可以快速直接。但是在索引中具有活动状态,在满足所有条件之前,引擎永远不需要转到任何实际数据页来检索数据。只有这样,它才会从正在 returned.
的 3 个相应 table 中提取记录听起来 posts_tags
是一个多对多映射 table?它需要两个索引:(post_id, tag_id)
和 (tag_id, post_id)
。其中之一可能应该是 PRIMARY KEY
(拥有 auto_increment id 是一种浪费并且会减慢速度)。另一个应该是INDEX
(不是UNIQUE
)。更多讨论:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#many_to_many_mapping_table
但是,为什么 posts_tag_two
和 posts_tag_one
都有?
除了那些'composite'键之外,还没有单列(post_id)
或(tag_id)
.
如果 tag
只是一个短字符串,请不要对它进行规范化;只需将它放在 table.
如需进一步讨论,请为每个 table 提供 SHOW CREATE TABLE
。还有 EXPLAIN SELECT ...
.