2017-10-05 49 views
0

用途:MySQL 5.7,整个数据集已知可缓存在Linux上的操作系统内存缓存中。 引擎是InnoDB的MySQL组即使索引缓慢,实际优化也没有意义

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' 
+0

你只比较'group by'或创建临时表并执行'group by'?另外,为什么你在第一个查询的'select'中有'date'? –

+0

你没有使用'EXPLAIN'。你没有使用'profiling'。所以,自然,不清楚MySQL在做什么。如果您使用了我提到的两种方法,那么您肯定会对优化程序的功能以及OS/MySQL在查找和发送数据时所做的操作有更清晰的了解。那么,试试看,然后回来更新? – Mjh

+0

删除“使用指数”,并使用解释已经建议。还考虑更改“日期> =?和日期<?”到 – iLikeMySql

回答

0

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

请注意,您不需要创建明确的临时表。你可以把第一次查询的子查询在FROM子句(称为派生表D):

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; 

注意的限制与大的多。我使用它阻止优化器将子查询合并到外部查询中。