MySQL查询太慢了,我该如何改进呢?

时间:2016-09-21 21:23:11

标签: mysql sql

我有这个查询,在约300.000行的表上需要大约14秒来提取数据。 该表将在不久的将来增加其规模......超过一百万行。 我使用了EXISTS子句而不是IN子句,并且我做了改进。 但查询太​​慢了。 你有解决方案吗? 提前谢谢。

这是查询:

SELECT 
    flow,
    COUNT(*) tot
FROM
    (
        SELECT 
            ff.session_id,
            GROUP_CONCAT(ff.page, '#', ff.snippet_params,'$',ff.is_lead SEPARATOR '|') flow 
            FROM table_a ff
            WHERE EXISTS 
                (
                    SELECT
                        f.session_id
                    FROM table_a f
                    WHERE f.session_id = ff.session_id
                    AND f.is_lead = 1
                    GROUP BY f.user_id 
                    ORDER BY f.user_id, f.`timestamp` 
                )
            GROUP BY ff.user_id 
            ORDER BY ff.user_id, ff.`timestamp`, ff.session_id 
    )
AS flow
GROUP BY flow 
ORDER BY tot DESC LIMIT 10

这是解释:

id  select_type         table       type    possible_keys       key         key_len  ref                              rows  Extra                                         
------  ------------------  ----------  ------  ------------------  ----------  -------  -----------------------------  ------  ----------------------------------------------
 1  PRIMARY             <derived2>  ALL     (NULL)              (NULL)      (NULL)   (NULL)                            532  Using temporary; Using filesort               
 2  DERIVED             ff          ALL     (NULL)              (NULL)      (NULL)   (NULL)                         322154  Using temporary; Using filesort               
 3  DEPENDENT SUBQUERY  f           ref     is_lead,session_id  session_id  767      ff.session_id       3  Using where; Using temporary; Using filesort  

4 个答案:

答案 0 :(得分:2)

ORDER BY中的额外表达没有任何意义,因为“GROUP BY user_id”将保证user_id的唯一值。

在<{em> ORDER BY操作之后应用GROUP BY操作。如果我的目的是为每个session_id获得最低user_id,我会使用MIN聚合。在原始查询中,ORDER BY对返回session_id没有任何影响。 session_id返回的值不确定。

(其他数据库会在此查询中引发错误.GROUP BY特定的扩展允许查询运行,但我们可以通过在sql_mode中包含ONLY_FULL_GROUP_BY来获得更多标准行为。)

EXISTS子查询中的GROUP BY没有任何意义。如果找到row,则存在一行。没有必要进行GROUP BY并聚合找到的行。

仔细观察,似乎没有必要在SELECT列表中返回session_id。 (在flow视图查询中,或在EXISTS子查询中。)

如果我们删除无关的语法并将查询简化为其本质,那么对于实际上重要的部分,我们会留下如下所示的查询:

 SELECT flow.flow  AS flow
      , COUNT(*)   AS tot
   FROM (
          SELECT GROUP_CONCAT(ff.page,'#',ff.snippet_params,'$',ff.is_lead SEPARATOR '|') AS flow
            FROM table_a ff
           WHERE EXISTS
                 ( SELECT 1
                     FROM table_a f
                    WHERE f.is_lead = 1
                      AND f.session_id = ff.session_id
                 )
           GROUP BY ff.user_id
        ) flow
  GROUP BY flow.flow
  ORDER BY tot DESC
  LIMIT 10

该查询基本上是为了从table_a获取所有行({1}},其中session_idtable_a中的至少一行匹配session_id的相同值is_lead 1}}其中user_id值为1。

然后获取所有找到的行,并根据 SELECT flow.flow AS flow , COUNT(*) AS tot FROM ( SELECT GROUP_CONCAT(ff.page,'#',ff.snippet_params,'$',ff.is_lead SEPARATOR '|') AS flow FROM ( SELECT d.session_id FROM table_a d WHERE d.is_lead = 1 GROUP BY d.session_id ) e JOIN table_a ff ON ff.session_id = e.session_id GROUP BY ff.user_id ) flow GROUP BY flow.flow ORDER BY tot DESC LIMIT 10 列中的值汇总它们。

GROUP_CONCAT中没有ORDER BY,这很奇怪,有点奇怪的是没有DISTINCT关键字。

GROUP_CONCAT聚合返回行的不确定顺序,并且可能包含重复值,这很奇怪。 (假设外部查询将根据从GROUP_CONCAT聚合返回的值执行另一个聚合。)

但是,我不确定这个查询应该回答什么问题。而且我不知道什么是独特的,什么不是。

我们知道EXISTS子查询可以重写为JOIN操作:

ORDER BY

我们可以努力使查询运行得更快。但在我这样做之前,我想确保查询返回一个与规范匹配的集合。我需要确保查询实际上回答了它旨在回答的问题。

我怀疑原始查询不正确。也就是说,我认为如果查询返回“正确”的结果,它会意外地这样做,而不是因为它保证了。或者因为表中行的唯一性(基数)或者由于处理行的意外顺序而存在特殊情况。

我想确保查询保证在我花时间调整它并添加索引之前返回正确的结果。

问:为什么GROUP_CONCAT中没有 GROUP_CONCAT( foo ORDER BY something) ? e.g。

 GROUP_CONCAT(DISTINCT foo ORDER BY something)

问:是否有特定原因没有DISTINCT关键字?

group_concat_max_length
问:我们是否应关注GROUP_CONCAT(静默)返回截断值的可能性? (基于 ... ON table_a (session_id, is_lead, page, snippet_params) 变量的设置?)

<强>后续

为了在上面的答案中最后一个查询的最佳表现,我建议添加以下索引:

session_id

或任何类似的索引,其中is_leadpage为前导列(按此顺序排列),还包括snippet_paramsflow列。如果将ORDER BY添加到GROUP_CONCAT,我们可能需要稍微不同的索引。

对于外部查询,导出的setUseSystemMenuBar列没有绕过“使用filesort”操作。 (除非您运行的是更新版本的MySQL,其中可能会创建索引。或者我们可以将查询分解为两个单独的操作。一个查询将内联视图实现为表,第二个查询运行反对。)

答案 1 :(得分:1)

在此子查询中,您使用的是group by但不具备聚合功能。

用于检查具有结果的EXIST {group =}基于group by或not是相同的..你应该删除组和顺序

f.session_id

这样

       WHERE EXISTS 
            (
                SELECT
                    f.session_id
                FROM table_a f
                WHERE f.session_id = ff.session_id
                AND f.is_lead = 1
                GROUP BY f.user_id 
                ORDER BY f.user_id, f.`timestamp` 
            )

查看您的查询我认为可以重构,例如:

          WHERE EXISTS 
            (
                SELECT
                    f.session_id
                FROM table_a f
                WHERE f.session_id = ff.session_id
                AND f.is_lead = 1
            )

答案 2 :(得分:0)

您需要确保将f.session_id和f.is_lead编入索引。它目前正在针对table_a的ff引用对中间结果中的每一行进行f表扫描。

答案 3 :(得分:0)

  • 摆脱计数(*),如果函数存在,MySQL无法再缓存查询,请尝试另一种方法。
  • 摆脱子查询,IIRC MySQL也不能缓存子查询。

很难给出此查询(或这些查询)的优化版本。您可能无法更改数据库结构,因此可以进行更简单的查询。也许某些缓存(redis等)用于其他值...