我正在使用Java EE上的MySql数据库进行Web项目。我们需要一个视图来汇总3个表中的数据,这些表的总行数超过3M。每个表都是用索引创建的。但是我还没有找到一种方法来从我们使用[group by]创建的视图中利用条件选择语句检索中的索引。
我收到一些人的建议,即在MySql中使用视图不是一个好主意。因为您无法像oracle中那样为mysql中的视图创建索引。但是在我进行的某些测试中,可以在视图选择语句中使用索引。也许我以错误的方式创建了这些视图。
我将用一个例子来描述我的问题。
我们有一个表格,用于记录NBA游戏中高分的数据,并在[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已扫描所有行以形成视图,然后在其中查找有条件的数据,而无需使用[happened_in]上的索引。
explain select * from v_kobe_highscores where happened_in = 2006; 解释结果
我们在项目中使用的视图是在具有数百万行的表之间构建的。在每个视图数据检索中扫描表中的所有行是不可接受的。请帮忙!谢谢!
@zerkms这是我在现实生活中测试的结果。我认为两者之间没有太大差异。我认为@ spencer7593有正确的观点。MySQL优化器不会在视图查询中“下推”谓词
如何让MySQL使用索引进行视图查询?简短的答案,提供了MySQL可以使用的索引。
在这种情况下,最佳索引可能是“覆盖”索引:
... ON highscores (player, happened_in, score) 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谓词进行评估。
请注意,存储了视图查询的所有行,(在您的情况下)是EVERY值的行happened_in,而不仅仅是您在外部查询中指定相等谓词的行。
某些人可能无法料想到视图查询的处理方式,这是与其他关系数据库处理视图查询的方式相比,在MySQL中使用“视图”会导致性能问题的一个原因。
使用合适的覆盖索引来提高视图查询的性能
给定您的视图定义和查询,您将获得的最好的视图访问方法是“使用索引”访问方法。为此,您需要一个覆盖索引,例如
... ON highscores (player, happened_in, score). 对于您现有的视图定义和现有查询,这可能是最有利的索引(从性能角度而言)。该player列是前导列,因为在视图查询中该列具有相等谓词。该happened_in列是下一个,因为在该列上有一个GROUP BY操作,而MySQL将能够使用该索引来优化GROUP BY操作。我们还包括该score列,因为这是查询中唯一引用的其他列。这使索引成为“覆盖”索引,因为MySQL可以直接从索引页面满足该查询,而无需访问基础表中的任何页面。这和我们要退出该查询计划一样好:“使用索引”而没有“使用文件排序”。
比较性能与没有派生表的独立查询
您可以将查询针对视图的执行计划与等效的独立查询进行比较:
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进行视图查询?
答:定义视图查询可以使用的合适的索引。
简短的答案是提供“覆盖索引”(索引包括视图查询中引用的所有列)。该索引中的前导列应该是用相等谓词引用的列(在您的情况下,该列player将是前导列,因为您player = 24在查询中有一个谓词。而且,在GROUP BY中引用的列也应该是前导列在索引中,它允许MySQL GROUP BY通过使用索引而不是使用排序操作来优化操作。
这里的关键是视图查询基本上是一个独立的查询。该查询的结果将存储在中间的“派生”表中(MyISAM表在对视图的查询运行时创建。
在MySQL中使用视图不一定是一个“坏主意”,但我强烈警告那些选择在MySQL中使用视图的人员,请注意MySQL如何处理引用这些视图的查询。MySQL处理视图查询的方式与其他数据库(例如Oracle,SQL Server)处理视图查询的方式(显着)不同。来源:stack overflow
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。