sql 加入顺序显着改变性能

sql join order dramatically changes performance

所以我在 mysql 中有两个表:articles 和 articles_rubrics,都有 ~20.000 行

文章有多个列,但其 article_id 已编入索引。

articles_rubrics 只有两个列:article_id 和 rubrics_id 并且两者都是单独索引的,最重要的是这两个列的联合索引。

我的问题是,当我 select 来自这些表的数据与连接时,顺序非常重要,这对我来说是个问题,我不明白其中的原因:

SELECT article_id,rubric_id FROM articles
LEFT JOIN articles_rubrics USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;

并解释说(articles_rubrics)这个:

time: 0.312 s
key_len: 1
ref: const
rows: 7352
extra: Using where; Using temporary; Using filesort

但是当我调换它的顺序时:

SELECT article_id,rubric_id FROM articles_rubrics
LEFT JOIN articles USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;

并解释说(articles_rubrics)这个:

time: 0.001 s
key_len:9
ref: NULL
rows: 28
extra: Using where; Using index

所以我有两个表,这使得他们的查询进行了约 300 次 slower/faster。这怎么可能?

PS:对于这个例子,我已经大大简化了我的现实世界问题,但我偶然发现了这个,因为我的

SELECT * FROM articles [LEFT JOIN for 5 other tables]

需要 1.5 秒,当我实际添加其他连接时,执行时间变为 0.006 秒。

显示索引:

show index from articles;
Table   Non_unique  Key_name    Seq_in_index    Column_name Collation   Cardinality Sub_part    Packed  Null    Index_type  Comment Index_comment
articles    0   PRIMARY 1   article_id  A   20043   NULL    NULL        BTREE        
articles    1   article_url_title   1   article_url_title   A   10021   NULL    NULL        BTREE        
articles    1   FULLTEXT    1   article_title   NULL    1   NULL    NULL        FULLTEXT         
articles    1   FULLTEXT    2   article_content NULL    1   NULL    NULL        FULLTEXT         

show index from articles_rubrics;
Table   Non_unique  Key_name    Seq_in_index    Column_name Collation   Cardinality Sub_part    Packed  Null    Index_type
articles_rubrics    0   PRIMARY 1   article_id  A   NULL    NULL    NULL        BTREE
articles_rubrics    0   PRIMARY 2   rubric_id   A   20814   NULL    NULL        BTREE
articles_rubrics    1   rubric_id   1   rubric_id   A   17  NULL    NULL        BTREE
articles_rubrics    1   article_id  1   article_id  A   20814   NULL    NULL        BTREE
SELECT article_id,rubric_id 
FROM articles
LEFT JOIN articles_rubrics USING(article_id)
WHERE rubric_id=1 <<<<<<<<<<<<<<<<<<<<<<<<<<< problem here
ORDER BY article_id DESC
LIMIT 10;

通过坚持从此查询返回的每一行都具有 rubric_id=1,您已经消除了 2 table 之间不匹配的任何行,因此没有必要使用左连接

SELECT a.article_id, ar.rubric_id 
FROM articles AS a
INNER JOIN articles_rubrics AS ar ON a.article_id = ar.article_id
WHERE ar.rubric_id = 1
ORDER BY a.article_id DESC
LIMIT 10;

您需要在每个引用中使用 table 或 table 别名。

数据库的连接操作是昂贵的过程。最好使用简单的 SELECT 嵌套。制作一个列表来存储数据,然后使用列表中的项目进行下一次查询。

这两个查询运行相同,唯一不同的是在两者中使用articles_rubrics中的article_id。

-- SELECT article_id,rubric_id FROM articles -- would be slow here
SELECT ar.article_id,ar.rubric_id FROM articles
JOIN articles_rubrics ar USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;

SELECT ar.article_id,ar.rubric_id FROM articles_rubrics ar
JOIN articles USING(article_id)
WHERE rubric_id=1
ORDER BY article_id DESC
LIMIT 10;

如果我强制 sql 服务器在结果中使用 articles_rubrics table,他会正确地决定实际上不需要这些文章。但是,服务器不会自动执行此操作,即使 article_id 用作密钥也是如此。

我仍然不完全理解为什么会这样(或者优化算法实际上是如何工作的),因为在这两种情况下,where rubric_id=1 进入 articles_rubrics table 并且在在这两种情况下,所选列已经存在(在这两种情况下,join articles 的存在又是 运行)。

但是,由于某些原因,在第一个示例中,服务器决定先加载所有文章,然后才检查每个文章的 rubric_id