MySql 性能查询与带有“解释”输出的视图

Posted

技术标签:

【中文标题】MySql 性能查询与带有“解释”输出的视图【英文标题】:MySql performance query vs view with 'explain' output 【发布时间】:2017-08-10 19:17:39 【问题描述】:

我试图理解为什么直接查询需要大约 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

视图的定义/查询是相同的,没有 '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 次?

【问题讨论】:

VIEWs 是语法糖。充其量,它们的运行速度与底层查询一样快;在最坏的情况下,他们无法进行您期望的优化。 WHERE 是选择要交付的数据的元素,当您的 VIEW 中缺少它时,在 VIEW 中不再可能通过仅检索所需行来实现性能,因为它没有在您的代码中使用. 【参考方案1】:

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

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

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

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

这也适用于derived tables and subqueries。

您正在寻求的行为是合并。这是默认值,MySQL 将尽可能尝试使用它。如果不可能(或者更确切地说:如果 MySQL 认为不可能),MySQL 必须评估完整的视图,无论您是否只需要其中一行。这显然需要更多时间,并且在您看来就是这样。

有一个列表阻止 MySQL 使用 merge 算法:

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

聚合函数(SUM()、MIN()、MAX()、COUNT() 等)

不同的

分组方式

拥有

限制

联合或联合所有

选择列表中的子查询

分配给用户变量

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

你可以测试一下 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 默认会尝试这样做,尽管它不适用于您的视图。

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

总结一下:MySQL 优化器目前还不够聪明。 不幸的是,您对此无能为力,除非测试 MySQL 是否接受 algorithm=merge,然后不使用 MySQL 无法合并的视图,而是自己合并它们。

【讨论】:

感谢您提供详细信息。这绝对是正在发生的事情。我尝试了 algorithm=merge 并收到 1354 警告。我不能切换dbs,所以我想我需要使用完整的查询。

以上是关于MySql 性能查询与带有“解释”输出的视图的主要内容,如果未能解决你的问题,请参考以下文章

MySQL视图查询超慢,求解答

MySQL视图查询超慢,求解答

需要帮助优化一个有趣的 MySQL 查询

mysql的查询表与查询视图的问题

MySQL 视图 - 性能不佳

MySQL InnoDB 与 MyISAM 中的复杂查询性能