MySql 性能查询与具有 'explain' 输出的 View

标签 mysql performance view

我试图理解为什么直接查询需要大约 0.5 秒才能运行,而使用相同查询的 View 需要大约 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;

查询 View :

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

为什么针对 View 的查询最终执行了 3 次全表扫描,而直接查询执行了大约 1.5 次?

最佳答案

简短的回答是:MySQL 优化器不够聪明,无法做到这一点。

processing a view , MySQL 可以合并 View 或为其创建一个临时表:

  • For MERGE, the text of a statement that refers to the view and the view definition are merged such that parts of the view definition replace corresponding parts of the statement.

  • For TEMPTABLE, the results from the view are retrieved into a temporary table, which then is used to execute the statement.

这与 derived tables and subqueries 的应用方式非常相似也是。

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

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

MERGE cannot be used if the view contains any of the following constructs:

  • Aggregate functions (SUM(), MIN(), MAX(), COUNT(), and so forth)

  • DISTINCT

  • GROUP BY

  • HAVING

  • LIMIT

  • UNION or UNION ALL

  • Subquery in the select list

  • Assignment to user variables

  • Refers only to literal values (in this case, there is no underlying table)

您可以测试 MySQL 是否会合并:尝试创建指定合并算法的 View :

create algorithm=merge view viewA as ...

如果 MySQL 认为它不能合并 View ,你会收到警告

1 warning(s): 1354 View merge algorithm can't be used here for now (assumed undefined algorithm)

在您的例子中,选择列表中的子查询 阻止了合并。这并不是因为不可能做到。您已经证明可以合并它:只需重写它即可。

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

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

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

关于MySql 性能查询与具有 'explain' 输出的 View ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45621890/

相关文章:

php - 如何使用 PHP 或 MySQL 对一个表中的不同列求和并在另一个表中获取求和结果?

iphone - iOS 中读取设备唯一 ID 的代码应该放在哪里?

javascript - JMeter 使用传入的参数来控制线程组

asp.net-mvc - 在 ASP.NET MVC 中将 ID 放在 <body> 上

ios - Swift - 在打开下一个 View 之前添加加载警报

SQL Developer 不为 View 获取行

MySQL - reshape 数据

mysql - 查找MySQL中同一列中两个值之间的差异

php - MySQL 中使用 join 跨数据库多条记录

c# - 在 Windows Phone 中以圆形显示图像