【问题标题】:sql join order dramatically changes performancesql 连接顺序极​​大地改变了性能
【发布时间】:2015-12-24 16:49:43
【问题描述】:

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

article 有多个 cols,但它的 article_id 已被索引。

articles_rubrics 只有两个列:article_id 和 rubrics_id,它们都是单独索引的,除此之外还有这两者的联合索引。

我的问题是,当我通过连接从这些表中选择数据时,顺序非常重要,这对我来说是个问题,我不明白它的原因:

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 倍。这怎么可能?

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

【问题讨论】:

  • 你能提供两个表上可用的索引吗? show index from table_name
  • 请检查:这肯定看起来不对....FROM articles_rubrics LEFT JOIN articles_rubrics USING(article_id) 它正在加入自己。此外,请在对列的所有引用(包括 where 子句)中使用表名或别名,性能可能取决于您引用的表
  • 谢谢!但是,由于 where 子句,您不需要 LEFT JOIN
  • 是的,我明白这一点,并将在我的代码中相应地修复它。我猜我对左连接太熟悉了。但是,EXPLAIN 中索引表的极端性能冲击和约 7000 个结果仍然存在。
  • EXPLAIN 应该有 2 行,而不是 1。

标签: mysql performance join database-performance


【解决方案1】:
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,您已经消除了两个表之间不匹配的任何行,因此使用 LEFT JOIN 没有任何意义

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;

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

【讨论】:

  • SELECT a.article_id,ar.rubric_id FROM articles a INNER JOIN articles_rubrics ar ON a.article_id = ar.article_id WHERE ar.rubric_id=1 ORDER BY a.article_id DESC LIMIT 10; 运行 0.2 秒,LEFT JOIN 运行 0.25 秒,但仍应运行 0.001 秒
  • 那条评论的目的是什么?
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    这两个查询运行相同,唯一的区别是两者都使用来自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 表,他会正确地确定这些文章实际上并不需要。但是,即使 article_id 用作键,服务器也不会自动执行此操作。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-10
      • 1970-01-01
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      相关资源
      最近更新 更多