遇到一個SQL執行很慢 SQL 如下:
SELECT
...
FROM
tableA
WHERE time >= 1492044535 and time <= 1492046335 GROUP by time, sourceName, serverSite,clientSite;
SELECT 部分忽略沒寫,是因為通常SQL執行慢不會跟這部分有關系,至少我沒見過。
該語句非常簡單,但是執行太慢。所以我們看一下執行計划
執行計划有幾個字段我們比較關注:
type:range
possible_keys:time
key:time
extra:using index condition; using temporary; using filesort
type 代表連接類型。range是索引范圍掃描的時候顯示的類型。
possible_keys 和 keys 是可用的索引以及實際的索引
extra 比較關鍵,我們詳細看一下這里的信息:
filesort
是說在排序的時候,沒辦法使用索引。比如我們這里用的索引是time,但group by 是 time, sourceName, serverSite,clientSite。Mysql有一點很重要是會默認按照group by排序。
group by time, sourceName, serverSite, clientSite
和
order by time, sourceName, serverSite, clientSite
開銷其實區別不大。所以這里排序不但要按照time 還要按照其它幾列
解決辦法
加order by null 這樣在group by的時候默認不排序,可以去掉filesort。 但實際測試發現還是慢,所以file sort不是性能關鍵。
using tempoaray
這里是說在mysql執行過程中產生了臨時表。這個操作比較耗時間。mysql的文檔列出了幾種會產生臨時表的語法,但和我們這里的情況都不相符合。倒是mariadb的文檔,雖然不是很詳細,但說明了我們的語句確實可能用到臨時表
A temporary table is created to hold the result. This typically happens if you are using GROUP BY, DISTINCT or ORDER BY.
仔細分析一下也有道理,用索引查到數據后,你需要對這些數據分組。這個過程肯定是在一個數據集上操作的,那么這個數據集應該就是臨時表了。不想要這個數據集的辦法就是取消這個分組操作。我們只需要create一個聯合索引
time,sourceName,serverSite,clientSite
這樣一個索引可以通過time過濾,天然按照分組的順序排序,就不用臨時表了。
同時可以在執行語句中加個force index強制執行這個索引。
這樣就沒有using temporary這個操作了