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

我正在使用Java EE上的MySql数据库处理Web项目。 我们需要一个观点,总结超过3M行的3个表格的数据。 每个表都是使用索引创build的。 但我还没有find一种方法来利用我们用[group by]创build的视图在条件select语句检索中的索引中find优势。

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

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

我们有一个表格,loggingNBA比赛中的高分数据,并在列[happend_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; 

我写了一个有条件的陈述来看看2006年 科比的最高得分;

 select * from v_kobe_highscores where happened_in = 2006; 

当我在蟾蜍解释mysql的时候,我发现mysql已经扫描了所有的行来形成视图,然后查找带有条件的数据,而没有在[happens_in]上使用索引。

 explain select * from v_kobe_highscores where happened_in = 2006; 

解释结果

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

@zerkms这是我在现实生活中testing的结果。 我没有看到太多的区别。 我认为@ spencer7593有正确的一点。 MySQL优化器不会在查询查询中“推”这个谓词。 真实的测试

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

在这种情况下,最佳指数可能是一个“覆盖”指数:

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

MySQL可能会使用该索引,而EXPLAIN将显示: "Using index"由于WHERE player = 24 (索引中的前导列上的等式谓词) GROUP BY happened_id (索引中的第二列) ,可以允许MySQL使用索引来优化索引以避免sorting操作,在索引中包括score列将允许查询完全从索引满足,而不必访问(查找)索引所引用的数据页面。

这是快速的答案。 较长的答案是,MySQL不太可能使用具有leading_id的前导列的视图查询的索引。


为什么视图导致性能问题

你在MySQL视图中遇到的一个问题是,MySQL不会将外部查询的谓词“推”到视图查询中。

你的外部查询指定WHERE happened_in = 2006 。 MySQL优化器在运行内部“查看查询”时不考虑谓词。 该查询的查询在外部查询之前单独执行。 该查询的执行结果集“物化”; 也就是说,结果存储为一个中间的MyISAM表。 (MySQL把它称为“派生表”,当你理解MysQL执行的操作时,它使用的名字是有意义的。)

底线是你已经定义的indices_in的索引没有被MySQL使用,当它返回形成视图定义的查询时。

在创build中间“派生表”之后,执行外部查询,使用“派生表”作为行源。 这是外部查询运行时,评估了happened_in = 2006谓词。

请注意,视图查询中的所有行都被存储起来,在你的情况下,这个行是EVERY_INDERY的一行,而不仅仅是在外部查询中指定一个相等谓词的行。

一些处理视图查询的方式可能是“意想不到的”,这是与其他关系数据库处理视图查询的方式相比,在MySQL中使用“视图”会导致性能问题的一个原因。


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

鉴于你的视图定义和你的查询,关于你将得到的最好的将是一个“使用索引”访问方法的视图查询。 为了得到这个,你需要一个覆盖索引,例如

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

对于您现有的视图定义和现有的查询,这可能是最有益的索引(性能明智)。 player列是领先的列,因为您在视图查询中的该列上有一个相等谓词。 下一个是happened_in列,因为你已经对该列进行了GROUP BY操作,MySQL将能够使用这个索引来优化GROUP BY操作。 我们还包括score列,因为这是您的查询中引用的唯一的其他列。 这使索引成为“覆盖”索引,因为MySQL可以直接从索引页面满足该查询,而无需访问基础表中的任何页面。 这就好像我们要摆脱查询计划:“使用索引”没有“使用filesort”。


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

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

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

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

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

但是不需要实现一个中间的MyISAM表。


我不确定以前的任何问题是否直接回答你所问的问题。

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

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

简短的答案是提供一个“覆盖索引”(索引包括视图查询中引用的所有列)。 该索引中的前导列应该是被相等谓词引用的列(在这种情况下,列player将成为前导列,因为在查询中有一个player = 24谓词)并且在GROUP BY中引用的列应该是索引中的前导列,它允许MySQL通过使用索引而不是使用sorting操作来优化GROUP BY操作。

这里的关键是视图查询基本上是一个独立的查询; 该查询的结果被存储在一个中间的“派生”表(一个MyISAM表,当对查询的查询运行时被创build。

在MySQL中使用视图并不一定是一个“坏主意”,但是我会强烈build议那些select在MySQL中使用视图的人来监视MySQL如何处理引用这些视图的查询。 MySQL处理查看查询的方式与其他数据库(例如Oracle,SQL Server)处理查询查询的方式(显着)不同。

在这种情况下,使用player + happened_in (按此特定顺序)列创build复合索引是最好的。

PS:不要在这么less的行上testingmysql优化器的行为,因为它可能比索引更适合fullscan。 如果你想看看现实生活中会发生什么 – 用真实的生活数据填充它。

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

我设置了一个PHP函数,我可以将参数发送到内部以最大化索引使用,而不是在视图外部的连接或where子句中使用它们。 在函数中,您可以为派生表制定SQL语法,并返回该语法。 然后在调用程序中,你可以做这样的事情:

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

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