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 性能查询与带有“解释”输出的视图的主要内容,如果未能解决你的问题,请参考以下文章