mysql查询速度慢,需要帮助才能优化

时间:2014-10-03 19:21:23

标签: mysql sql

table1

time            userid  id1 id2 
9/1/2014 3:30   user1   123 555
9/1/2014 3:32   user1   123 555
9/1/2014 3:13   user1   123 555
9/1/2014 3:15   user1   123 555
9/1/2014 3:38   user2   321 555
9/1/2014 3:21   user2   321 555
9/1/2014 3:38   user2   456 666
9/1/2014 3:21   user2   456 666

table2

id1 orderid
321 order1
123 order2

解释查询:

select_type table   type possible index  key         key_len    ref        row     Extra
SIMPLE     table1   ALL                                                    934420   Using where; Using temporary; Using filesort
SIMPLE     table2   ref lookupindex    lookupindex    33        table1.id1  1   

我的table1有大约10亿行,table2是有20k行的查找表,而555行是大约1亿行。 id2约占table1总数的10%。 table2基本上是具有所有id1的查找表。 ID1-> orderid有多对一的关系。换句话说,一个id1只属于一个orderid。 除了userid之外,table2和table1没有null值。

我想为每个orderid计算唯一身份用户。

我的查询需要很长时间才能运行(没有在5小时内完成,所以我停止了),我不知道除了索引之外如何优化它。我在table2.id1上有索引。

select  table2.orderid, count(distinct userid)
from table1 left join table2 on table1.id1 = table2.id1
where table1.id2="555"
group by table2.orderid

mysql首先执行左连接或首先执行where语句吗?应该将订单555存储到不同的表中然后运行它们查询吗?

4 个答案:

答案 0 :(得分:2)

问题是你的独特操作,一个是非常昂贵的。您可以通过在userid上放置索引来提高效率,这两个键也应该有一个索引。我不确定你在功能上做了什么,但也许其他东西也可以替代。

答案 1 :(得分:1)

这基本上是您的查询:

select t2.orderid, count(distinct t1.userid)
from table1 t1 left join
     table2 t2
     on t1.id1 = t2.id1
where t1.id2 = 555
group by t2.orderid;

首先,您可能不需要left join,因为您要按第二个表中的列进行分组。如果table1非常大,这可能会有很大帮助。因此,编写没有该连接的查询:

select t2.orderid, count(distinct t1.userid)
from table1 t1 join
     table2 t2
     on t1.id1 = t2.id1
where t1.id2 = 555
group by t2.orderid;

其次,您需要table1(id2, id1, userid)table2(id1, orderid)上的索引。

可能还有其他一些优化,具体取决于数据的性质。例如,table1包含多个useriddistinctjoin的工件吗?

答案 2 :(得分:0)

首先,你计算所有不同的值userid和id1(没有连接),然后将table1中的计数值与table2连接

SELECT orderid, a.sum 
FROM table2 
INNER JOIN     
(SELECT id1, COUNT(DISTINCT userid) as sum FROM table1 WHERE id2 = '555' GROUP BY id1) a
ON table2.id1 = a.id1

答案 3 :(得分:0)

问:mysql首先执行左连接还是先执行where语句?应该将订单555存储到不同的表中然后运行它们查询吗?

理论上,优化器可以自由选择产生指定结果的任何执行计划。优化器应该足够聪明,可以选择它认为最有效的操作顺序。

实际上,我们编写语句的方式以及我们提供的索引可能会对MySQL可用的选项产生重大影响。


要查看MySQL正在选择的执行计划,我们可以使用EXPLAIN。这向我们展示了MySQL将要执行的操作的总结。

Understanding the Query Execution Plan

拥有适当的索引可以为MySQL提供更高效的访问路径。

没有看到EXPLAIN输出,或者表的定义,以及可用的索引,我们只是在猜测。

鉴于该声明非常缓慢,我们将冒险猜测合适的索引不可用,其次,MySQL正在花费大量时间在“{filesort”操作上{{1操作。)

也可能重写该语句以返回等效结果,或者几乎等效的结果。我们可以抛出一些建议“试试这个”或“试试”。

但是让我们了解MySQL需要执行的操作。

首先,GROUP BY列上有一个等式谓词。如果这是相当有选择性的(少于id2中总行数的10%或20%,table1table1作为前导列的索引可能会提供有效的访问权限,这可能会带来一些性能上的好处。(这很有效,因为MySQL可以对索引使用范围扫描操作来快速缩小请求的行,而不必查看表中的每个翻转行。)

其次,在您的查询中,有一个“外部联接”操作,用于查找id2中的匹配行,并在table2列上使用等式谓词。因此,id1上的table2作为主要列的索引可能会有所帮助。

该查询还从id1的匹配行访问orderid列;如果我们还在索引中包含该列,那将使它成为“覆盖索引”,这只是一种简短的说法,即MySQL将能够直接从索引中检索所需的所有值,而无需查找到基础表格中的页面。

如果检索到很多行,我们可能会花很多时间对它们进行排序(GROUP BY所需的排序操作。)

我们没有很多信息,关于orderid列的基数,列列是否为null,userid列的基数,是否为null,我们期待的行数要退还,等等。


在我们开始调整此特定语句之前,我认为我们需要了解此查询试图回答的问题,并确保此查询实际上会返回您正在寻找的答案。我们应该开放探索是否可以从不同的查询中返回等效答案。

看起来你想要一个来自table2的{​​{1}}的明确列表(包括可能的NULL值),而不是所有这些,只有一个子集,符合某些标准。

除了orderid值之外,您还需要来自table1中table2列中具有特定值的行的计数(不同orderid值的数量)。< / p>

例如,如果我们不关心userid ...

的NULL值

(即,当table1中的行没有table2中的匹配行时,由于外连接而由原始查询产生的NULL值... id2中的每一行在orderid中没有匹配的行,我们知道table1将为NULL ...)

除了NULL orderid的计数之外,以下查询将返回相同的orderid列表并计数......

table2

为了获得该查询的最佳性能,我建议在table2上覆盖索引:

table2.orderid

和table1上的覆盖索引,或者两者之一:

SELECT b.orderid
     , COUNT(DISTINCT a.userid)
  FROM table2 b
  JOIN table1 a
    ON a.id1 = b.id1
   AND a.id2 = '555'
 WHERE b.orderid IS NOT NULL
 GROUP BY b.orderid

(我们可能会让MySQL执行紧密索引扫描操作以满足GROUP BY,而不是昂贵的临时表(“使用filesort;使用临时”)

我们真正希望看到的是ON table2 (orderid, id1) 对该查询和原始查询的输出。

(如果我们确实需要ON table1 (id2, id1, userid) ON table1 (id1, id2, userid) 的{​​{1}}值的计数,我们可以编写另一个查询来单独获取它。)