21

查看查询的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 使用临时
 1 SIMPLE Q ref PRIMARY,SS 2 const 204 使用索引
 1 SIMPLE V ref PRIMARY,n,QQ 5 const,db.Q.QID 6 使用 where;使用索引;清楚的
 1 SIMPLE R1 ref PRIMARY,LL 154 const,db.V.VID 447 使用索引;清楚的
 1 SIMPLE W eq_ref PRIMARY,w PRIMARY 5 const,db.R.R.RID,const 1 使用 where;清楚的
 1 SIMPLE R2 eq_ref PRIMARY,L PRIMARY 156 const,db.W.RID,const 1 使用 where;清楚的

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

  • 由于它的主键完全匹配,因此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)(似乎没有更好的索引可以在该表上使用(假设它已经完全使用主键来为每个结果返回一行?)。

应该如何解释所有这些信息?我猜对了吗,因为这样的输出过滤发生在要连接的最终表上,与更早加入表并更快地过滤这些行相比,浪费了大量的精力?如果是这样,如何确定何时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
4

1 回答 1

16

取决于您要做什么以及查询是什么。

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

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

通常,您应该尽量避免看到Using filesortor Using temporary,尽管如果您不期待它们,它们只会是不好的。

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

Using temporary当它引用派生表时可能会很糟糕,因为它们没有索引。看来您已经显式创建了一个带有索引的临时表,所以在这里,它还不错。有时,您唯一的选择是使用派生表,因此Using temporary.

于 2012-04-13T22:57:33.160 回答