MySQL Group即使在索引时也很慢,而实际的优化没有任何意义

时间:2017-10-05 15:03:06

标签: mysql sql performance

用途:MySQL 5.7,已知整个数据集缓存在Linux上的OS memcache中。 引擎是InnoDB

SELECT colA, colB, count(*)
FROM someTable use index (idx_someTable_Date_colA_colB)
WHERE Date >= ? and Date < ?
GROUP BY colA, colB;

上述的性能是非线性的,并且很快会降低到比下一个查询构造慢10倍的速度:

DROP TEMPORARY TABLE IF EXISTS TEMPT;
CREATE TEMPORARY TABLE TEMPT (
  colA bigint(20),
  colB varchar(50)
) 
AS (
  SELECT colA, colB
  FROM someTable use index (idx_someTable_Date_colA_colB)
  WHERE Date >= ? and Date < ?
);
SELECT colA, colB, count(*)
FROM TEMPT
GROUP BY colA, colB;

我找不到任何合理的解释,为什么对于大型数据集,第二种查询样式应该比第一种查询样式快10倍。首先,原始数据具有多列索引,涵盖了确切的感兴趣列。其次,更快的版本需要创建一个单独的表,该表至少复制所有数据一次,然后对未编制索引的临时表进行表扫描。

为什么第一个构造非常慢,而第二个构造的性能相当于Postgresql ??

热门查询的个人资料:

# Status, Duration
'starting', '0.000046'
'Waiting for query cache lock', '0.000012'
'starting', '0.000013'
'checking query cache for query', '0.000164'
'checking permissions', '0.000022'
'Opening tables', '0.000054'
'init', '0.000077'
'System lock', '0.000025'
'optimizing', '0.000048'
'statistics', '0.000206'
'preparing', '0.000068'
'Creating tmp table', '0.000172'
'Sorting result', '0.000032'
'executing', '0.000030'
'Sending data', '48.525629'
'Creating sort index', '0.016266'
'end', '0.000042'
'query end', '0.000030'
'removing tmp table', '0.001459'
'query end', '0.000024'
'closing tables', '0.000020'
'freeing items', '0.000052'
'cleaning up', '0.000049'

底部查询的配置文件(创建临时/插入):

# Status, Duration
'starting', '0.000310'
'checking permissions', '0.000034'
'checking permissions', '0.000019'
'Opening tables', '0.000098'
'init', '0.000256'
'creating table', '0.023076'
'After create', '0.000056'
'System lock', '0.000038'
'optimizing', '0.000037'
'statistics', '0.000274'
'preparing', '0.000058'
'executing', '0.000017'
'System lock', '0.000040'
'Sending data', '3.877377'
'Waiting for query cache lock', '0.000047'
'Sending data', '0.000017'
'end', '0.000012'
'query end', '0.000705'
'closing tables', '0.000031'
'freeing items', '0.000070'
'cleaning up', '0.000038'

底部查询的配置文件(从临时中选择):

# Status, Duration
'starting', '0.000069'
'Waiting for query cache lock', '0.000018'
'starting', '0.000009'
'checking query cache for query', '0.000102'
'checking permissions', '0.000025'
'Opening tables', '0.000016'
'init', '0.000111'
'System lock', '0.000036'
'optimizing', '0.000020'
'statistics', '0.000051'
'preparing', '0.000049'
'Creating tmp table', '0.000090'
'Sorting result', '0.000045'
'executing', '0.000016'
'Sending data', '0.273446'
'Creating sort index', '0.002288'
'end', '0.000052'
'query end', '0.000027'
'removing tmp table', '0.000022'
'query end', '0.000017'
'closing tables', '0.000018'
'freeing items', '0.000064'
'cleaning up', '0.000057'

1 个答案:

答案 0 :(得分:0)

查看详细的EXPLAIN输出(最好是JSON格式,因为它有更多细节)以了解发生了什么将会很有帮助。不同性能的一个原因可能是在两种情况下使用不同的分组/聚合算法。可以在分组之前进行排序,或者可以在读取行时逐步增加临时表中的计数。这两种方法可能会有不同的表现。

请注意,您无需创建显式临时表。您可以将第一个查询放在FROM子句中的子查询中(称为派生表):

SELECT colA, colB, count(*)
FROM (
  SELECT colA, colB
  FROM someTable use index (idx_someTable_Date_colA_colB)
  WHERE Date >= ? and Date < ?
  LIMIT 100000000
) dt
GROUP BY colA, colB;

注意LIMIT的数量很大。我用它来阻止优化器将子查询合并到外部查询中。