【问题标题】:MySQL sorting with Using temporary; Using filesortMySQL使用临时排序;使用文件排序
【发布时间】:2016-09-04 03:56:44
【问题描述】:

这是我要启动的查询:

SELECT c.creative_id, c.creative_title, c.creative_image_name, c.gravity, c.ad_strength
FROM creatives AS c
INNER JOIN term_relationships AS tr ON c.creative_id = tr.creative_id
WHERE tr.term_id
IN ( 14, 1, 50, 76, 104 )
GROUP BY c.creative_id
HAVING COUNT(tr.term_id ) =5
ORDER BY c.gravity ASC 
LIMIT 30;

这是EXPLAIN 查询的输出:

这是creatives 表结构:

CREATE TABLE `creatives` (
  `creative_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `scraper_id` bigint(20) unsigned DEFAULT NULL,
  `creative_title` varchar(255) NOT NULL,
  `creative_image_name` varchar(255) DEFAULT NULL,
  `image_attrib` varchar(12) DEFAULT NULL,
  `original_image_name` varchar(255) DEFAULT NULL,
  `creative_subtext` varchar(255) DEFAULT NULL,
  `dest_url` varchar(2083) NOT NULL,
  `lp_url` varchar(2083) NOT NULL,
  `lp_image_name` varchar(255) DEFAULT NULL,
  `lp_image_flag` tinyint(1) unsigned NOT NULL DEFAULT '0',
  `creative_first_seen` date NOT NULL,
  `creative_last_seen` date NOT NULL,
  `daily_ad_count` int(5) unsigned NOT NULL,
  `ad_strength` int(11) unsigned NOT NULL,
  `prev_ad_strength` int(11) unsigned DEFAULT NULL,
  `gravity` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`creative_id`),
  KEY `gravity` (`gravity`)
) ENGINE=InnoDB AUTO_INCREMENT=173037591 DEFAULT CHARSET=utf8

我担心Using temporary; using filesort 在另一列上同时启动GROUP BYORDER BY。如果我删除ORDER BY,临时文件和文件排序就会消失,查询运行得非常快。

什么我不明白,为什么mysql需要临时表,为什么不能先where filter + sort by c.gravity,然后按结果表分组并根据HAVING子句过滤。过滤后的表格将按c.gravity正确排序,因为在分组和过滤后重力值保持不变。

我尝试了什么:

  1. 选择没有 ORDER BY 的所有内容,包装到子查询中并再次加入 creatives 表 - 结果相同,使用临时、文件排序和慢速

  2. 尝试添加 FORCE USE INDEX FOR ORDER BY (gravity) 并没有改变任何东西。 EXPLAIN 和执行时间保持不变。

更新@Rick 已经回答了这个问题,并且使用他的相关子查询而不使用GROUP BY 确实要快得多。我在这里为查询添加EXPLAIN 输出:

以及SHOW CREATE TABLE term_relationships 的输出以及新创建的索引:

还有一个要问@Rick 的问题:为什么我们需要带有c3 的外部查询?似乎只是单独加入creatives,只是为了从其他列中获取值并按重力对记录进行排序。但是,它们已经使用内部查询进行了排序,我们可以轻松地在 c1 中添加缺失的列:

SELECT  c1.creative_id,c1.creative_title,c1.creative_image_name,c1.gravity, c1.ad_strength
            FROM  creatives AS c1
            WHERE  
              ( SELECT  COUNT(*)
                    FROM  term_relationships
                    WHERE  c1.creative_id = creative_id
                      AND  term_id IN ( 14, 1, 50, 76, 104 )
              ) = 5 
            ORDER BY  c1.gravity ASC
            LIMIT  30;

我的理解正确还是我在您的查询中遗漏了什么?

【问题讨论】:

  • (关于c3)没有c3,只有creative_id 被拖到tmp 表中。使用c3,可以拖拽很多列。如果LIMIT (30) 与LIMITing 之前的行数有很大差异,则可能存在明显的性能差异。

标签: mysql indexing query-optimization query-performance


【解决方案1】:

临时表和文件排序本身并不是坏人。这就是它们的体积。

这可能看起来更复杂,但可能更快:

SELECT  c3.creative_id,
        c3.creative_title, c3.creative_image_name,
        c3.gravity, c3.ad_strength
    FROM  
      ( SELECT  creative_id
            FROM  creatives AS c1
            WHERE  
              ( SELECT  COUNT(*)
                    FROM  term_relationships
                    WHERE  c1.creative_id = creative_id
                      AND  term_id IN ( 14, 1, 50, 76, 104 )
              ) = 5 
            ORDER BY  c1.gravity ASC
            LIMIT  30
      ) AS c2
    JOIN  creatives c3 USING (creative_id)
    ORDER BY  c3.gravity 

如果它碰巧使用INDEX(gravity) 进行内部查询,那么它将在找到包含所有 5 个事务的 30 行后停止。如果它生成一个 tmp 表,它将只有 30 行——比原始查询要好得多。还要注意,tmp 表会更窄——只有creative_id 会在其中。最后它返回到creatives 以获取所需的其余列。最后,还会有另一种排序,但只有 30 行。

此外,“文件排序”通常是 RAM 中非常快速的排序,而不是真正的“文件”排序。我很确定我的查询不会在磁盘上。

term_relationships 需要这个复合索引:INDEX(creative_id, term_id)

【讨论】:

  • 谢谢瑞克,我们今天要试试。我会让你知道,乍一看它真的很快。那我也看看你的评论。非常感谢!
  • 更新:我们对其进行了测试并更改了代码以使用它 - 再次感谢。顺便说一句,在不实际使用 group by 的情况下编写 group by + count() 的方式非常有趣!我还尝试添加您在terms_relationships(creative_id, term_id) 上提到的索引,但根据EXPLAIN 输出没有使用它,所以不确定我们是否需要它来进行这个查询。
  • 这是一个“相关子查询”,它只需要一个计数,因此没有GROUP BY。让我们看看EXPLAIN SELECT ...;我对不使用该索引感到困惑。现在“够快”了吗?
  • 在 UPDATE 之后查看我对原始问题的编辑。我还有一个问题 - 也许我错了,在 term_relationships 上添加索引后的解释输出。
  • PRIMARY KEY (a,b)KEY(a,b) 是多余的;后者几乎是无用的。 (我猜我不知道你已经PK了。)
猜你喜欢
  • 2016-09-27
  • 2012-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多