如何基于EXPLAIN计划优化MySQL查询

时间:2012-04-13 21:58:25

标签: mysql query-optimization

查看查询的EXPLAIN计划,如何确定最佳优化位置?

我理解首先要检查的是,是否正在使用好的索引,但除此之外,我有点难过。通过过去的反复试验,我有时发现连接的顺序可以是一个很好的改进来源,但是如何通过查看执行计划来确定?

虽然我非常希望对如何优化查询(建议阅读非常赞赏!)有一个很好的总体理解,但我也意识到,讨论具体案例通常比抽象的谈话更容易。因为我正在用这个撞到墙上,所以你的想法会非常感激:

id   select_type   table   type     possible_keys    key       key_len   ref                    rows   Extra
 1   SIMPLE        S       const    PRIMARY,l,p,f4   PRIMARY         2   const                     1   Using temporary
 1   SIMPLE        Q       ref      PRIMARY,S        S               2   const                   204   Using index
 1   SIMPLE        V       ref      PRIMARY,n,Q      Q               5   const,db.Q.QID            6   Using where; Using index; Distinct
 1   SIMPLE        R1      ref      PRIMARY,L        L             154   const,db.V.VID          447   Using index; Distinct
 1   SIMPLE        W       eq_ref   PRIMARY,w        PRIMARY         5   const,db.R.RID,const      1   Using where; Distinct
 1   SIMPLE        R2      eq_ref   PRIMARY,L        PRIMARY       156   const,db.W.RID,const      1   Using where; Distinct

我在解释执行计划的最后一行时是否正确如下:

  • 因为它的主键完全匹配,每个输出行只需要获取一行R2;
  • 然而,这些输出行会根据适用于R2
  • 的某些条件进行过滤

如果是这样,我的问题在于在最后一步中发生的过滤。如果条件导致没有过滤(例如WHERE `Col_1_to_3` IN (1,2,3)),则查询运行得非常快(~50ms);但是,如果条件限制所选行(WHERE `Col_1_to_3` IN (1,2)),则查询需要相当长的时间(~5s)。如果限制是单个匹配(WHERE `Col_1_to_3` IN (1)),则优化器会建议一个完全不同的执行计划(执行时间略好于5秒,但仍然比50毫秒差很多)。似乎没有一个更好的索引可以在该表上使用(假设它已经完全使用主键为每个结果返回一行?)。

如何解释所有这些信息?我是否正确地猜测,因为这样的输出过滤发生在要加入的最终表上,相比之前加入表并且更快地过滤这些行会浪费相当大的努力?如果是,那么如何确定执行计划R2何时应该加入?

虽然我拒绝包括查询&这里的模式已经完整(因为我很可能知道要寻找什么,而不仅仅是被告知答案),我理解有必要推进讨论:

SELECT DISTINCT
    `Q`.`QID`
FROM
    `S`
    NATURAL JOIN `Q`
    NATURAL JOIN `V`
    NATURAL JOIN `R` AS `R1`
    NATURAL JOIN `W`

    JOIN `R` AS `R2` ON (
            `R2`.`SID` = `S`.`SID`
        AND `R2`.`RID` = `R1`.`RID`
        AND `R2`.`VID` = `S`.`V_id`
        AND `R2`.`Col_1_to_3` IN (1,2) -- this is where performance suffers!
    )

WHERE
    AND `S`.`SID` = @x
    AND `W`.`WID` = @y
;

R的定义是:

CREATE TABLE `R` (
  `SID` smallint(6) unsigned NOT NULL,
  `RID` smallint(6) unsigned NOT NULL,
  `VID` varchar(50) NOT NULL DEFAULT '',
  `Col_1_to_3` smallint(1) DEFAULT NULL,
  `T` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`SID`,`RID`,`VID`),
  KEY `L` (`SID`,`VID`,`Col_1_to_3`),
  CONSTRAINT `R_f1` FOREIGN KEY (`SID`) REFERENCES `S` (`SID`),
  CONSTRAINT `R_f2` FOREIGN KEY (`SID`, `VID`) REFERENCES `V` (`SID`, `VID`),
  CONSTRAINT `R_f3` FOREIGN KEY (`SID`, `VID`, `Col_1_to_3`) REFERENCES `L` (`SID`, `VID`, `LID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

1 个答案:

答案 0 :(得分:14)

取决于您的目标和查询内容。

通常,对于EXPLAIN中具有Using where的每一行,您需要使用索引(possible keyskeys列)。这些是您的过滤器,包括WHERE和ON。说它Using index甚至更好。这意味着它有一个覆盖索引,MySQL可以直接从索引中检索数据,而不是访问表数据中的行。

应该查看没有Using where的行,并返回大量行。这些是表中所有行的返回值。我不知道你的疑问是什么,所以我不知道这里是否要惊慌。尝试过滤结果集以减小大小并提高性能。

您通常应该尽量避免看到Using filesortUsing temporary,但如果您不期待它们,这些只会很糟糕。

Filesort通常与ORDER子句一起出现。您通常希望MySQL使用覆盖索引(Using index),以便从服务器按顺序返回行。如果他们不是,那么MySQL必须在之后使用filesort命令它们。

Using temporary引用派生表时可能会很糟糕,因为它们没有索引。您似乎已经明确地创建了一个带索引的临时表,所以在这里,它并不坏。有时,您唯一的选择是使用派生表,因此Using temporary