通过Join-Where-Group更正索引通过选择查询避免使用临时;使用filesort

时间:2013-01-22 12:17:14

标签: mysql join group-by

我已经搜索了很多关于下面描述的案例的解决方案,但不幸的是我没有找到类似的案例。

我有以下情况: (作为一个新用户,该网站拒绝了我的照片,但我可以通过邮件发送。下面是它的文字表示)

Table 1 "swap_plan"          Table 2 "cell"
ClusterName | SiteID         SiteID    | Cell      |  Time       | Counter
-----------------------      ---------------------------------------------
Cluster A   | SiteID A1      SiteID A1 | Cell A1-1 | day1        | 5
Cluster A   | SiteID A2      SiteID A1 | Cell A1-1 | day2        | 3
Cluster A   | SiteID A3      SiteID A1 | Cell A1-1 | day3        | 6
Cluster A   | SiteID A4      SiteID A1 | Cell A1-2 | day1        | 6
Cluster A   | SiteID A5      SiteID A1 | Cell A1-2 | day2        | 2
Cluster A   | SiteID A6      SiteID A1 | Cell A1-2 | day3        | 9
.......................      ..............................................
Cluster B   | .........      ..............................................

(Where No 1)      (ON Clause "SiteID")            (Where No 2)    Sum(Counter)

我必须显示一些性能指标(表2“单元格”中的“计数器”),随时间汇总(表2“单元格”中的“时间”)和集群(表1“swap_plan”中的“ClusterName”)。

通过两个表“SiteID”的公共列完成连接。请注意,在表2“单元格”中,每个SiteID由3个不同的对象(“单元格”)组成。所以,实际上我为每个Cell做了“Counter”的SUM()。

查询如下:

SELECT ClusterName,Time,SUM(counter)
FROM cell
INNER JOIN swap_plan ON swap_plan.Siteid = cell.Siteid
WHERE ClusterName='Cluster A' AND Time>=day1 AND Time<=day2
GROUP BY Time

列类型如下:

表1“交换计划”:

  1. ClusterName - CHAR(30)
  2. SiteID - VARCHAR(10)
  3. 表2“细胞”:

    1. SiteID - VARCHAR(10)
    2. 时间 - 日期时间
    3. Counter - INT
    4. “解释”显示如下:

      table          type    key           key_len      ref               rows  Extra
      
      swap_plan      ref     Index 1       30           const             31    Using where; Using index; Using temporary; Using filesort
      cell           ref     Index_siteid  13           swap_plan.SiteID  368   Using where
      

      使用的索引如下:

      swap_plan:索引1(1. ClusterName和2. SiteID)

      cell:Index_siteid(SiteID)

      优化程序看起来的行数很少,这很好:

      swap_plan:6066中的31个和单元格:6.6 mil中的368个。

      我的问题是这些“使用临时;使用filesort”。据我所知,这来自Group By所需的排序(如果我删除它,这些过程不会根据Explain执行)。我发现为了避免它们,你需要在分组的列上有一个索引。我有一个特殊的索引,只包括“时间”列,但是这个没有使用,即使提示“USE INDEX FOR GROUP BY()”。

      因此,我的查询运行速度不够快 - 大约需要15秒(比如15个SiteID和10个日期),我需要将此持续时间减少到至少一半。

      我的主要问题是:

      • 完全可以删除“使用临时;使用filesort”或    减少执行所需的时间? (我试着增加    读缓冲区大小为16MB,没有效果)
      • 在JOIN情况下我需要什么样的索引定义,在WHERE子句中我在不同的表中按2列过滤,在ON子句中按第3列过滤
      • 我可以应用什么样的组优化(索引等)?

      非常感谢你!

1 个答案:

答案 0 :(得分:0)

我会这样写查询:

SELECT c.time
     , SUM(c.counter)
     , MAX(p.clustername) AS clustername
  FROM cell c

  JOIN swap_plan p
    ON p.siteid      = c.siteid
   AND p.clustername = 'Cluster A'

 WHERE c.time  >=  'day1'
   AND c.time  <=  'day2'
 GROUP
    BY c.time

我一定要在cell上有一个索引,time是前导列。

MySQL可以使用相同的索引来满足范围谓词(在WHERE子句中),并满足GROUP BY,而无需执行“使用文件排序”操作。

... ON cell (time)

根据列的大小,覆盖索引可能会提供最佳性能。覆盖索引包含查询中引用的表中的所有列,因此可以完全从索引页面满足查询,而无需查找基础表中的页面。

... ON cell (time, siteid, counter)

对于swap_plan上的索引,我有一个索引,其中site_id作为前列,并且包括clustername列,其中之一:

... ON swap_plan (clustername, site_id)

... ON swap_plan (site_id, clustername)

这两个列的组合可能会受到UNIQUE约束,即,site_id的值对于给定的clustername将是不同的。 (如果不是这种情况,并且同一(site_id,clustername)元组出现多次,则总计counter的总值可能会被夸大。

我要寻找EXPLAIN输出,以显示swap_plan的值和const(字面的“集群A”)值对c.siteid表的'ref'查找集群名称。


对于具有31行和368行的表,我们不会在最佳执行计划和可怕的执行计划之间看到性能(经过时间)的显着差异。

当两个表中的任何一个都可扩展到数百万行时,差异就会变得明显。执行计划的优化程序选择受每个表的统计信息(大小,行数,列基数)影响,因此执行计划可能会随着表大小的增加而改变。