Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么在MYSQL中WHERE语句中的子查询速度如此之慢?_Mysql - Fatal编程技术网

为什么在MYSQL中WHERE语句中的子查询速度如此之慢?

为什么在MYSQL中WHERE语句中的子查询速度如此之慢?,mysql,Mysql,下面的查询需要1秒多一点的时间来完成,并返回24k个结果 SELECT DISTINCT (ad_name) FROM marketing.fbk_ad_stats_daily AS marketing WHERE date_start >= subdate(CURRENT_DATE,30) and spend >1 当我在以下WHERE语句中将所述查询用作条件时: SELECT name, tracking_key FROM

下面的查询需要1秒多一点的时间来完成,并返回24k个结果

    SELECT DISTINCT
    (ad_name)
FROM
    marketing.fbk_ad_stats_daily AS marketing
WHERE
    date_start >= subdate(CURRENT_DATE,30)
    and spend >1
当我在以下WHERE语句中将所述查询用作条件时:

SELECT
    name,
    tracking_key
FROM
    marketing.ads
WHERE
    name IN
             (
             SELECT DISTINCT
                 (ad_name)
             FROM
                 marketing.fbk_ad_stats_daily
             WHERE
                 date_start >= subdate(CURRENT_DATE,30))
它运行了几十分钟,我关闭了它,然后才看到它实际需要多长时间。但是,当我获取原始查询的结果并将其作为WHERE语句中的一个条件时,就像这样

SELECT
    name,
    tracking_key
FROM
    marketing.ads
    where name 
in ('bnj-fbk-m-us-5db72043 c18 - MF-Image18-US-OS-Android',
'bnj-fbk-m-us-5db72043 c17 - MF-Image17-US-OS-Android',
'bnj-fbk-m-us-f72f73c8 c33 - MF-Image33-US-OS-Android',
'bnj-fbk-m-us-f72f73c8 c35 - MF-Image35-US-OS-Android',
'bnj-fbk-m-us-5db72043 c6 - MF-Image6-US-OS-Android', ... etc... x 24k rows... )
我得到了3秒的极快运行时间。 如何解释这两种方法之间的差异?为什么第二个查询不是两个查询的线性组合

第二个查询的解释是:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  <subquery2> ALL (null)  (null)  (null)  (null)  (null)  (null)
1   SIMPLE  ads ALL (null)  (null)  (null)  (null)  826919  Using where; Using join buffer (Block Nested Loop)
2   MATERIALIZED    fbk_ad_stats_daily  range   ix_fbk_ad_stats_daily_date_start    ix_fbk_ad_stats_daily_date_start    6   (null)  399630  Using index condition

但是我没有足够的理解来辨别答案,因为MySQL优化器正在为语句生成一个次优的计划,所以速度很慢

我们可以使用MySQL
EXPLAIN
语句查看执行计划的详细信息


为了回答这个问题,在最坏的情况下,MySQL可能会将该子查询视为一个依赖的相关子查询,这样外部查询处理的每一行都会执行该子查询

SELECT t.name
     , t.tracking_key 
  FROM marketing.ads t
 WHERE EXISTS ( SELECT 1
                  FROM marketing.fbk_ad_stats_daily d
                 WHERE d.date_start >= CURRENT_DATE() + INTERVAL -30 DAY
                   AND d.spend      > 1
                   AND d.ad_name    = t.name    /*correlated to outer row*/
              )
也就是说,MySQL执行计划可能会为外部查询获取一行,然后检查
WHERE
子句中的条件。为此,MySQL可能正在执行子查询,将子查询的结果具体化到一个临时ish表中。请注意,DISTINCT关键字将导致MySQL执行唯一排序,以消除重复项。一旦结果准备好,MySQL就可以扫描结果,看看是否找到了外部行中的值。根据MySQL优化器的版本,可能没有索引。如果未找到匹配项,则丢弃外部查询中的行

然后MySQL从外部查询中获取下一行,并执行相同的过程。执行子查询,具体化结果,并扫描该子查询以查看
name
的值

这可能是大型集合最糟糕的执行计划

或者,可能只是子查询被具体化了一次,但没有索引,因此子查询中的每一行都必须被扫描以查找与外部查询中的每一行相匹配的
名称
。对于子查询返回的24000行,对于被丢弃的外部查询中的每一行,可能有24000个字符串匹配

另一种可能是MySQL正在等待获得锁,例如,表是MyISAM,并且存在持有表锁的并发DML操作


我们是想更多地解释性能低下的可能原因,还是应该跳转到其他查询模式以获得更好的性能

应考虑的一些建议:

  • 联接操作或
    子查询存在
    而不是子查询中的
  • 限定所有列引用
  • 不要包括无关的父母

演示如何使用联接操作将查询结果具体化为派生表。较新版本中对MySQL优化器的改进将允许在派生表上自动创建索引,以提高性能。但是,如果派生表是连接的驱动表,MySQL可以使用
name
作为前导列的索引。例如,查询的覆盖索引将是
。。。在marketing.ads上(姓名、跟踪键)

有时,
存在
模式将提供适当的性能,并提供适当的索引。请注意,子查询与外部行相关,子查询中的
ad_name
值需要与外部查询中的
name
值匹配

SELECT t.name
     , t.tracking_key 
  FROM marketing.ads t
 WHERE EXISTS ( SELECT 1
                  FROM marketing.fbk_ad_stats_daily d
                 WHERE d.date_start >= CURRENT_DATE() + INTERVAL -30 DAY
                   AND d.spend      > 1
                   AND d.ad_name    = t.name    /*correlated to outer row*/
              )

此表单的查询需要检查
t
中的每一行,因此子查询将针对外部查询检查(而不是丢弃)的每一行执行。

当您编写此问题时,是否有任何建议弹出,其中包含类似
使用EXPLAIN分析您的查询的内容?“第二个查询的解释结果如下:“是什么?两个查询的问题中的解释输出在查询中发布执行计划,而不是注释。但是,
DISTINCT
只会导致延迟。您不关心子查询中的单个值,仅当值存在或不存在时。没有理由为此执行
DISTINCT
操作
DISTINCT
需要使用所有结果,对其排序并消除重复数据。那很贵。第二个查询将昂贵的操作与另一个结果集相关联。服务器现在有两个选项-执行DISTINCT和与外部表联接。或者对外部表中的每个值执行不同的操作。这可能就是这里发生的情况。如果
名称
广告名称
没有正确的索引或统计信息,服务器很容易选择错误的执行计划。因此,如果我理解,并且您所说的与此相匹配。。。“在上述嵌套查询中,必须为每个员工重新执行内部查询。”。。。。。对marketing.ads中存在的每一行执行我的子查询?与它只执行一次、存储结果并由marketing.ads中的每一行进行检查相反?@JoshFlori:是的,对相关子查询的解释就是我想说的。。。一个可能的解释是性能缓慢。我没有看到子查询实际上是相关的(列引用是不合格的,因此让我猜测哪些列在哪些表中……MySQL优化器可以找到它,因为它可以快速查找表定义,这对人类读者来说是一件麻烦事。这只是一种可能的解释。我不认为MySQL 5.6、5.7或8.0优化器会生成相关的sub查询计划对于不相关的子查询)“最坏的情况是,MySQL可以将该子查询视为一个依赖的相关子查询,这样它就可以在每一行过程中执行
SELECT t.name
     , t.tracking_key 
  FROM marketing.ads t
 WHERE EXISTS ( SELECT 1
                  FROM marketing.fbk_ad_stats_daily d
                 WHERE d.date_start >= CURRENT_DATE() + INTERVAL -30 DAY
                   AND d.spend      > 1
                   AND d.ad_name    = t.name    /*correlated to outer row*/
              )