同一表上的SQL三重联接速度很慢

时间:2019-06-12 12:26:46

标签: mysql sql performance

我下面有一张桌子

 CREATE TABLE `xcpRush2_SandraTriplets` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
  `idConceptStart` int(11) NOT NULL,
  `idConceptLink` int(11) NOT NULL,
  `idConceptTarget` int(11) NOT NULL,
  `flag` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_name` (`idConceptStart`,`idConceptLink`,`idConceptTarget`),
  KEY `idConceptStart` (`idConceptStart`,`idConceptLink`,`idConceptTarget`),
  KEY `idConceptStart_4` (`idConceptStart`),
  KEY `idConceptTarget` (`idConceptTarget`),
  KEY `idConceptLink` (`idConceptLink`,`idConceptTarget`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

数据看起来像数据库小提琴:https://www.db-fiddle.com/f/ejXP7qgvwNqAZeuaN3DFNz/3

如您所见,它已在几列上完全索引。

在我的桌子上,我有大约80万个idConceptStart满足满足

的条件
idConceptLink = 5 idConceptTarget = 14500 AND
idConceptLink = 3 idConceptLink = 14504 AND
idConceptLink = 12 idConceptLink = 11

执行此查询时

SELECT * FROM  xcpRush2_SandraTriplets l    
   JOIN  xcpRush2_SandraTriplets link1 ON link1.idConceptStart = l.idConceptStart  
   JOIN  xcpRush2_SandraTriplets link2 ON link2.idConceptStart = link1.idConceptStart

    WHERE 
       l.idConceptLink = 5  AND 
       l.idConceptTarget = 14500 AND 
       l.flag != 1 AND 

       link2.flag != 1 AND 
       link2.idConceptLink = 3 AND 
       link2.idConceptTarget = 14504 AND 

       link1.flag != 1 AND 
       link1.idConceptTarget = 12 AND 
       l.idConceptLink = 11  

    ORDER BY l.idConceptStart DESC  LIMIT 10 

这是SQL解释 SQL Explain

查询大约需要30秒(!)呈现

但是如果我删除了这个(并且只有这个)

 link2.idConceptLink = 3 AND link2.idConceptTarget =14504

然后查询需要20毫秒的时间呈现

    SELECT * FROM  xcpRush2_SandraTriplets l    
   JOIN  xcpRush2_SandraTriplets link1 ON link1.idConceptStart = l.idConceptStart  
   JOIN  xcpRush2_SandraTriplets link2 ON link2.idConceptStart = l.idConceptStart 
   WHERE 
      l.idConceptLink = 5 AND 
      l.idConceptTarget = 14500 AND 
      l.flag != 1  AND 

      link2.flag != 1 AND       

      link1.flag != 1 AND 
      link1.idConceptTarget = 12 AND 
      link1.idConceptLink = 11  

    ORDER BY l.idConceptStart DESC  LIMIT 10 

SQL Explain

我很困惑,因为该表在idConceptLinkidConceptTarget上建立了索引,并且分别进行的每个查询都非常快地呈现了<20 ms

查询中的每对idConceptLinkidConceptTarget对都返回大量行(不仅link2.idConceptLink = 3 AND link2.idConceptTarget = 14504)

您能帮我确定瓶颈吗?

修改

在评论中发现更多问题后,问题似乎在ORDER BY上。取决于我是否加入l.idConceptStart或link1.idConceptStart或link2.idConceptStart,查询速度很慢。在我的实际情况下,ORDER BY link2.idConceptStart 很慢。

索引结构如下

CREATE TABLE `xcpRush2_SandraTriplets` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `idConceptStart` int(11) NOT NULL,
  `idConceptLink` int(11) NOT NULL,
  `idConceptTarget` int(11) NOT NULL,
  `flag` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `idx_name` (`idConceptStart`,`idConceptLink`,`idConceptTarget`),
  KEY `idConceptStart` (`idConceptStart`),
  KEY `idConceptTarget` (`idConceptTarget`),
  KEY `idConceptLink` (`idConceptLink`,`idConceptTarget`)
) ENGINE=InnoDB AUTO_INCREMENT=5747878 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

索引是

xcprush2_sandratriplets 0   PRIMARY 1   id  A   5207892 NULL    NULL        BTREE       
xcprush2_sandratriplets 0   idx_name    1   idConceptStart  A   1243366 NULL    NULL        BTREE       
xcprush2_sandratriplets 0   idx_name    2   idConceptLink   A   5207936 NULL    NULL        BTREE       
xcprush2_sandratriplets 0   idx_name    3   idConceptTarget A   5207936 NULL    NULL        BTREE       
xcprush2_sandratriplets 1   idConceptStart  1   idConceptStart  A   1122352 NULL    NULL        BTREE       
xcprush2_sandratriplets 1   idConceptTarget 1   idConceptTarget A   123870  NULL    NULL        BTREE       
xcprush2_sandratriplets 1   idConceptLink   1   idConceptLink   A   5   NULL    NULL        BTREE       
xcprush2_sandratriplets 1   idConceptLink   2   idConceptTarget A   154480  NULL    NULL        BTREE

Indexes

查询时

 SELECT  l.idConceptStart, l.idConceptLink, l.`idConceptTarget` FROM  xcpRush2_SandraTriplets l  JOIN  xcpRush2_SandraTriplets link1 ON link1.idConceptStart = l.idConceptStart  JOIN  xcpRush2_SandraTriplets link2 ON link2.idConceptStart = l.idConceptStart 
    WHERE l.idConceptLink = 5  
    AND l.idConceptTarget = 14500
    AND l.flag != 1 
     AND link1.flag != 1 AND 
            link1.idConceptTarget =14504 AND link1.idConceptLink = 3 AND link2.flag != 1 AND 
            link2.idConceptTarget =12 AND link2.idConceptLink = 11  ORDER BY  link2.idConceptStart DESC  LIMIT 1000 OFFSET 0

这里是EXPLAIN结构

1   SIMPLE  link1   NULL    ref idx_name,idConceptStart,idConceptTarget,idConceptLink   idConceptTarget 4   const   1611256 18.00   Using where; Using temporary; Using filesort
1   SIMPLE  l   NULL    eq_ref  idx_name,idConceptStart,idConceptTarget,idConceptLink   idx_name    12  sandra.link1.idConceptStart,const,const 1   90.00   Using where
1   SIMPLE  link2   NULL    eq_ref  idx_name,idConceptStart,idConceptTarget,idConceptLink   idx_name    12  sandra.link1.idConceptStart,const,const 1   90.00   Using where

Slow explain

查询时快速

SELECT  l.idConceptStart, l.idConceptLink, l.`idConceptTarget` FROM  xcpRush2_SandraTriplets l  JOIN  xcpRush2_SandraTriplets link1 ON link1.idConceptStart = l.idConceptStart  JOIN  xcpRush2_SandraTriplets link2 ON link2.idConceptStart = l.idConceptStart 
    WHERE l.idConceptLink = 5  
    AND l.idConceptTarget = 14500
    AND l.flag != 1 
     AND link1.flag != 1 AND 
            link1.idConceptTarget =14504 AND link1.idConceptLink = 3 AND link2.flag != 1 AND 
            link2.idConceptTarget =12 AND link2.idConceptLink = 11  ORDER BY  l.idConceptStart DESC  LIMIT 1000 OFFSET 0

这里是EXPLAIN结构

   1    SIMPLE  l   NULL    index   idx_name,idConceptStart,idConceptTarget,idConceptLink   idConceptStart  4   NULL    13036   3.08    Using where
1   SIMPLE  link1   NULL    eq_ref  idx_name,idConceptStart,idConceptTarget,idConceptLink   idx_name    12  sandra.l.idConceptStart,const,const 1   90.00   Using where
1   SIMPLE  link2   NULL    eq_ref  idx_name,idConceptStart,idConceptTarget,idConceptLink   idx_name    12  sandra.l.idConceptStart,const,const 1   90.00   Using where

fastExplain

编辑2

排序的最佳表似乎是随机的。现在,我几个小时后运行了相同的查询(发生了一些插入),但是使用相同的查询,解决键顺序的结构发生了变化。快速查询变成慢查询,而慢查询变成快速查询。如果我通过l.idConcept订购,请进行以下解释

explain edit 2

键的表解析顺序似乎是随机的。我完全迷路了。最后,我唯一需要的就是首先获取最后一个数据库条目

2 个答案:

答案 0 :(得分:1)

“完全索引”-不。您有一些索引,包括一些多余的索引。

这可能是查询的最佳索引:

INDEX(link, target, start)

让我们谈谈flag。它有多少个不同的值?如果只有2(例如0和1),则更改为flag = 0而不是flag != 1。与=相比,Optimizer更适合处理!=测试。并更改为INDEX(link, target, flag, start)

有百分之几的行具有flag = 1?这可能会引起更多想法。

您有UNIQUE键和代理人id吗?您是否从其他任何表中引用了id?如果不是,请摆脱它,并将UNIQUE提升为PRIMARY KEY。但是到那时,我希望重新排列该PK中的列以符合我的建议。

一些建立索引的规则:

  • 首先输入=(以linktarget(以任意顺序)进行测试的列
  • 如果希望索引中还包含WHERE列,最好让索引完全处理!=ORDER BY正在停止该索引)( s)。如果还存在LIMIT,则尤其如此。
  • UNIQUE(a,b,c)排除了对INDEX(a,b,c)的需求
  • INDEX(a,b)排除了对INDEX(a)的需求。
  • 更多:http://mysql.rjweb.org/doc.php/index_cookbook_mysql

答案 1 :(得分:0)

通过使用     通过l.idConceptStart DESC命令 而不是     ORDER BY link2.idConceptStart DESC 您可以避免每次解释临时存储和文件排序 并且可以减少访问的ROWS。

查看我的个人资料,网络个人资料以获取联系信息。