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

Rog*_*Ray 54 mysql indexing

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

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

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

我们有一张桌子记录了NBA比赛中的高分数据,其中的索引在[eventsnd_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;
Run Code Online (Sandbox Code Playgroud)

插入数据(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);
Run Code Online (Sandbox Code Playgroud)

然后我创建了一个视图,看看科比每年得到的最高分

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;
Run Code Online (Sandbox Code Playgroud)

我写了一篇有条件的声明,看看2006年科比得到的最高分;

select * from v_kobe_highscores where happened_in = 2006;
Run Code Online (Sandbox Code Playgroud)

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

explain select * from v_kobe_highscores where happened_in = 2006;
Run Code Online (Sandbox Code Playgroud)

解释结果

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

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

spe*_*593 46

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

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

... ON highscores (player, happened_in, score)
Run Code Online (Sandbox Code Playgroud)

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

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


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

您对MySQL视图的一个问题是,MySQL不会将谓词从外部查询"推送"到视图查询中.

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

最重要的是,happened_in当MySQL破坏构成视图定义的查询时,MySQL所使用的索引并未被使用.

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

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

处理视图查询的方式可能是某些人"意外",这是与其他关系数据库处理视图查询的方式相比,使用MySQL中的"视图"可能导致性能问题的一个原因.


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

根据您的视图定义和查询,关于您将获得的最佳信息将是视图查询的"使用索引"访问方法.要做到这一点,你需要一个覆盖索引,例如

... ON highscores (player, happened_in, score).
Run Code Online (Sandbox Code Playgroud)

对于现有视图定义和现有查询,这可能是最有利的索引(性能明智).该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
Run Code Online (Sandbox Code Playgroud)

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

... ON highscores (player, happened_in, score)
Run Code Online (Sandbox Code Playgroud)

但无需实现中间MyISAM表.


我不确定以前的任何一个都能直接回答你提出的问题.

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

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

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

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

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