MySql性能查询vs带有'explain'输出的视图

时间:2017-08-10 19:17:40

标签: mysql performance view

我试图理解为什么直接查询需要大约0.5秒才能运行,但使用相同查询的视图需要大约10秒才能运行。 MySql v5.6.27。

直接查询:

select 
    a,b, 
    (select count(*) from TableA i3 where i3.b = i.a) as e,
    func1(a) as f, func2(a) as g
from TableA i
where i.b = -1 and i.a > 1500;

直接查询'解释'结果:

id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,PRIMARY,i,range,PRIMARY,PRIMARY,4,\N,3629,Using where
2,DEPENDENT SUBQUERY,i3,ALL,\N,\N,\N,\N,7259,Using where

View的定义/查询是相同的,没有'where'子句...

select 
    a,b,
    (select count(*) from TableA i3 where i3.b = i.a) as e,
    func1(a) as f, func2(a) as g
from TableA i;

查询视图:

select * from ViewA t where t.b = -1 and t.a > 1500;

查询“解释”结果:

id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,PRIMARY,<derived2>,ALL,\N,\N,\N,\N,7259,Using where
2,DERIVED,i,ALL,\N,\N,\N,\N,7259,\N
3,DEPENDENT SUBQUERY,i3,ALL,\N,\N,\N,\N,7259,Using where

为什么针对视图的查询最终执行3次全表扫描,而直接查询执行~1.5?

1 个答案:

答案 0 :(得分:0)

简短的回答是:MySQL优化器不够聪明。

processing a view时,MySQL可以合并视图或为其创建临时表

  
      
  • 对于MERGE,合并了引用视图和视图定义的语句的文本,以便视图定义的某些部分替换语句的相应部分。

  •   
  • 对于TEMPTABLE,视图的结果将被检索到临时表中,然后用于执行该语句。

  •   

这适用于与derived tables and subqueries非常相似的方式。

您正在寻找的行为是 merge 。这是默认值,MySQL会尽可能尝试使用它。如果不可能(或者更确切地说:如果MySQL 认为它是不可能的话),MySQL必须评估完整视图,无论你是否只需要一行。这显然需要更多时间,并且在您的视图中会发生这种情况。

有一些阻止MySQL使用 merge 算法的东西:

  

如果视图包含以下任何构造,则不能使用MERGE:

     
      
  • 聚合函数(SUM(),MIN(),MAX(),COUNT()等)

  •   
  • DISTINCT

  •   
  • GROUP BY

  •   
  • HAVING

  •   
  • LIMIT

  •   
  • UNION或UNION ALL

  •   
  • 选择列表中的子查询

  •   
  • 分配给用户变量

  •   
  • 仅指文字值(在这种情况下,没有基础表)

  •   

如果MySQL合并与否,您可以测试它:尝试创建指定合并算法的视图:

create algorithm=merge view viewA as ...

如果MySQL认为它不能合并视图,则会收到警告

  

1警告:1354此处暂不能使用视图合并算法(假设未定义的算法)

在您的情况下,选择列表中的子查询阻止了合并。这不是因为它不可能做到。你已经知道可以合并它:只需重写它。

但MySQL优化器没有看到这种可能性。它不是特定于视图:如果您直接使用未合并的视图代码,它实际上不会合并它:explain select * from (select a, b, ... from TableA i) as ViewA where ...。你必须在MySQL 5.7上进行测试,因为MySQL 5.6原则上不会在这种情况下合并(因为在查询中,它假设你想要在这里有一个temptable,即使对于非常简单的派生可以合并的表格。默认情况下,MySQL 5.7会尝试执行此操作,但它不适用于您的视图。

随着优化器得到改进,在某些情况下,即使在选择列表中存在子查询的情况下,优化器也会合并,因此该列表有一些例外。基于MySQL的MariaDB实际上在合并优化方面要好得多,并且会像你一样合并你的视图 - 所以即使作为一台机器也可以这样做。

总而言之:MySQL优化器目前还不够聪明。 遗憾的是,除了测试MySQL是否接受algorithm=merge然后不使用MySQL无法合并的视图而是自己合并它们之外,你不能做太多的事情。