【问题标题】:How to optimise MySQL queries based on EXPLAIN plan如何根据 EXPLAIN 计划优化 MySQL 查询
【发布时间】:2012-04-26 06:59:08
【问题描述】:

查看查询的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,S S 2 const 204 使用索引 1 SIMPLE V ref PRIMARY,n,Q Q 5 const,db.Q.QID 6 使用 where;使用索引;清楚的 1 SIMPLE R1 ref PRIMARY,L L 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)),则查询需要相当长的时间(约 5 秒)。如果限制是单个匹配 (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

【问题讨论】:

  • 你介意也显示查询吗?
  • @MarcusAdams:我不介意,但你会寻找什么?我觉得如果我知道你会看什么,我可能会学到更多……
  • 您指的是 col_1_to_3,但我在 EXPLAIN 结果中没有看到这样的列。如果您可以将问题表述为仅与解释有关,换句话说,删除讨论查询的段落,那么我们不需要查询,答案是肯定的。一般来说,我们需要查询、模式和解释,否则我们只是在猜测。
  • 嗯.. R2.col_1_to_3 确实不在 EXPLAIN 结果中;你希望它出现在哪里?如果可能的话,我真的很想知道您在查询和架构中要寻找的什么...否则下次我在这里时不会再获悉位置!
  • @MarcusAdams:根据要求在上面包含查询和架构。

标签: mysql query-optimization


【解决方案1】:

取决于你的目标和查询是什么。

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

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

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

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

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

【讨论】:

  • 谢谢马库斯。我想我觉得最奇怪的是决赛桌上的过滤器在性能上的显着差异。因此,您建议我看的问题似乎不在于“...返回大量行的行”?
猜你喜欢
  • 2021-08-09
  • 2016-06-11
  • 2021-11-16
  • 1970-01-01
  • 2015-09-14
  • 2012-08-06
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
相关资源
最近更新 更多