如何让MySQL使用INDEX进行查看查询?

时间:2012-12-19 03:09:11

标签: mysql indexing

我正在使用Java EE上的MySql数据库开展Web项目。我们需要一个视图来总结来自3个表格的数据,总体上超过3M行。每个表都是使用索引创建的。但是我没有找到一种方法来利用我们用[group by]创建的视图中的条件选择语句检索中的索引中的优势。

我得到了人们的建议在MySql中使用视图不是一个好主意。因为你不能像在oracle中那样为mysql中的视图创建索引。但是在我进行的一些测试中,索引可以在view select语句中使用。也许我以错误的方式创建了这些观点。

我将用一个例子来描述我的问题。

我们有一张表记录了NBA比赛中得分高的数据,其中的索引在[发布_in]

CREATE  TABLE `highscores` (
   `tbl_id` int(11) NOT NULL auto_increment,
   `happened_in` int(4) default NULL,
   `player` int(3) default NULL,
   `score` int(3) default NULL,
   PRIMARY KEY  (`tbl_id`),
   KEY `index_happened_in` (`happened_in`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入数据(8行)

INSERT INTO highscores(happened_in, player, score)
VALUES (2006, 24, 61),(2006, 24, 44),(2006, 24, 81),
(1998, 23, 51),(1997, 23, 46),(2006, 3, 55),(2007, 24, 34), (2008, 24, 37);
然后我创建了一个视图,看看科比每年得到的最高得分

CREATE OR REPLACE VIEW v_kobe_highScores
AS
   SELECT player, max(score) AS highest_score, happened_in
   FROM highscores
   WHERE player = 24
   GROUP BY happened_in;

我写了一份条件声明,以查看 kobe 2006 中获得的最高分;

select * from v_kobe_highscores where happened_in = 2006;

当我在forad中为mysql解释它时,我发现mysql已经扫描所有行以形成视图,然后在其中查找具有条件的数据,而不使用[happen_in]上的索引。

explain select * from v_kobe_highscores where happened_in = 2006;

explain result

我们在项目中使用的视图是在具有数百万行的表中构建的。在每个视图数据检索中扫描表中的所有行是不可接受的。请帮忙!谢谢!

@zerkms这是我在现实生活中测试的结果。我没有看到很多区别。我认为@ spencer7593有正确的观点。 MySQL优化器不会在视图查询中“推送”该谓词。 real-life test

3 个答案:

答案 0 :(得分:42)

如何让MySQL使用索引进行视图查询?简短的回答,提供MySQL可以使用的索引。

在这种情况下,最佳指数可能是"覆盖" index:

... ON highscores (player, happened_in, score)

MySQL可能会使用该索引,并且由于"Using index"(索引中前导列的等式谓词),EXPLAIN将显示:WHERE player = 24。{{ 1}}(索引中的第二列),可以允许MySQL使用索引来优化它以避免排序操作。在索引中包含GROUP BY happened_id列将允许查询完全从索引中得到满足,必须访问(查找)索引引用的数据页。

这是快速回答。更长的答案是MySQL不太可能使用带有score前导列的索引来进行视图查询。


为什么视图会导致性能问题

你对MySQL视图的一个问题是MySQL没有"推送"从外部查询到谓词查询的谓词。

您的外部查询指定happened_id。 MySQL优化器在运行内部"视图查询"时不考虑谓词。视图的查询在外部查询之前单独执行。执行该查询的结果集得到了实现&#34 ;;也就是说,结果存储为中间MyISAM表。 (MySQL称之为"派生表",当您理解MysQL执行的操作时,他们使用的名称是有意义的。)

最重要的是,MySQL在查询形成视图定义的查询时没有使用您在WHERE happened_in = 2006上定义的索引。

中间"派生表"创建,然后执行外部查询,使用"派生表"作为行源。当外部查询运行时,happened_in谓词被评估。

请注意,视图查询中的所有行都会被存储,(在您的情况下)是每个happened_in = 2006值的行,而不仅仅是您在外部查询中指定了等式谓词的行。

处理查看查询的方式可能是"意外"一些人,这是使用"观点"与其他关系数据库处理视图查询的方式相比,在MySQL中可能会导致性能问题。


使用合适的覆盖索引提高视图查询的性能

鉴于您的视图定义和您的查询,关于您将获得的最佳结果将是"使用索引"视图查询的访问方法。为此,您需要一个覆盖索引,例如

happened_in

对于现有视图定义和现有查询,这可能是最有利的索引(性能明智)。 ... ON highscores (player, happened_in, score). 列是前导列,因为视图查询中的该列具有等式谓词。接下来是player列,因为您已经对该列进行了GROUP BY操作,并且MySQL将能够使用此索引来优化GROUP BY操作。我们还包含happened_in列,因为这是查询中引用的唯一其他列。这使得索引成为"覆盖"索引,因为MySQL可以直接从索引页面满足该查询,而无需访问基础表中的任何页面。这就像我们要退出查询计划一样好:"使用索引"没有"使用filesort"。


将性能与没有派生表的独立查询进行比较

您可以将查询的执行计划与视图与等效的独立查询进行比较:

score

独立查询还可以使用覆盖索引,例如

SELECT player
     , MAX(score) AS highest_score
     , happened_in
 FROM highscores
WHERE player = 24
  AND happened_in = 2006
GROUP
   BY player
    , happened_in

但无需实现中间MyISAM表。


我不确定之前的任何内容都能直接回答您提出的问题。

问:如何让MySQL使用INDEX进行查看查询?

A:定义视图查询可以使用的合适INDEX。

简短的回答是提供覆盖索引" (索引包括视图查询中引用的所有列)。该索引中的前导列应该是使用等式谓词引用的列(在您的情况下,列... ON highscores (player, happened_in, score) 将是一个前导列,因为您在查询中有一个player谓词。 GROUP BY中引用的列应该是索引中的前导列,这允许MySQL通过使用索引而不是使用排序操作来优化player = 24操作。

这里的关键点是视图查询基本上是一个独立的查询;该查询的结果存储在一个中间的"派生的" table(在对视图的查询运行时创建的MyISAM表。

在MySQL中使用视图不一定是一个坏主意"但我强烈提醒那些选择使用MySQL中的视图的人要知道MySQL如何处理引用这些视图的查询。 MySQL处理视图查询的方式与其他数据库(例如Oracle,SQL Server)处理视图查询的方式(显着)不同。

答案 1 :(得分:2)

在这种情况下,您可以使用player + happened_in(按此特定顺序)列创建复合索引。

PS:不要在如此少量的行上测试mysql优化器行为,因为它可能更喜欢全扫描而不是索引。如果你想看看现实生活中会发生什么 - 用真实的数据来填充它。

答案 2 :(得分:1)

这并没有直接回答这个问题,但对于遇到此问题的其他人来说,这是一个直接相关的解决方法。这实现了使用视图的相同好处,同时最大限度地减少了缺点。

我设置了一个PHP函数,我可以向其发送参数,将内容推入内部以最大化索引使用,而不是在视图外的join或where子句中使用它们。在该函数中,您可以为派生表制定SQL语法,并返回该语法。然后在调用程序中,您可以执行以下操作:

$table = tablesyntax(parameters);
select field1, field2 from {$table} as x... + other SQL

因此,您可以获得视图的封装优势,能够将其称为视图,而不是索引限制。