mysql - 同一张表上的 SQL 三重连接很慢

标签 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;

数据在 db fiddle 中看起来像:https://www.db-fiddle.com/f/ejXP7qgvwNqAZeuaN3DFNz/3

如您所见,它已在多个列上完全编入索引。

在我的 table 上我有大约 800k 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 毫秒

此外,查询中的每个 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

这是解释结构

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

这是解释结构

   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

排序的最佳表似乎是随机的。现在我在几个小时后运行相同的查询(发生了一些插入)但是使用相同的查询解决键顺序的结构发生了变化。快查询变成慢查询,慢查询变成快查询。如果我 ORDER BY l.idConceptStart 以下说明

explain edit 2

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

最佳答案

“完全索引”——不。您有几个索引,包括一些冗余索引。

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

INDEX(link, target, start)

让我们谈谈标志。它有多少种不同的值?如果只有 2(比如 0 和 1),则更改为 flag = 0 而不是 flag != 1。与 != 相比,优化器更擅长处理 = 测试。并更改为 INDEX(link, target, flag, start)

flag=1 的行百分比是多少?这可能会引发更多想法。

你有 UNIQUE 键,还有一个代理 id?您是否从任何其他表中引用了 id?如果不是,去掉它,并将 UNIQUE 提升为 PRIMARY KEY。但那时,我希望重新排列该 PK 中的列以符合我的建议。

建立索引的一些规则:

  • 将使用 = 测试的列放在第一位(linktarget,顺序不限)
  • 如果您希望索引也包括ORDER BY 列。如果还有 LIMIT,则尤其如此。
  • UNIQUE(a,b,c) 排除了对 INDEX(a,b,c)
  • 的需要
  • INDEX(a,b) 排除了对 INDEX(a) 的需要。
  • 更多:http://mysql.rjweb.org/doc.php/index_cookbook_mysql

关于mysql - 同一张表上的 SQL 三重连接很慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56562220/

相关文章:

java - 这个 Java 基准测试发生了什么?

php - 从数据库返回 0 时替换获取的结果

MySQL,按天分组上个月的数据?

sql - where 子句中的连接字符串是否可 SARGable?

mysql - ORDER BY soundex 与 WHERE (MySql)

c++ - 指针容器与对象容器 - 性能

python - 按键(整数)对字典进行有效排序,返回排序的值列表

PHP 将用户表链接到另一个表

php - 只显示最新的数据库表内容

php - 引号和参数化 SQL 查询的问题