2012-10-23 61 views
8

這感覺就像是「爲我做我的作業」類問題,但我真的堅持在這裏試圖使這個查詢快速運行在許多行的表上。 Here's a SQLFiddle顯示模式(或多或少)。爲數億行的表優化查詢

我玩過索引,試圖找到能夠顯示所有需要的列的東西,但沒有取得太大的成功。這裏的create

CREATE TABLE `AuditEvent` (
    `auditEventId` bigint(20) NOT NULL AUTO_INCREMENT, 
    `eventTime` datetime NOT NULL, 
    `target1Id` int(11) DEFAULT NULL, 
    `target1Name` varchar(100) DEFAULT NULL, 
    `target2Id` int(11) DEFAULT NULL, 
    `target2Name` varchar(100) DEFAULT NULL, 
    `clientId` int(11) NOT NULL DEFAULT '1', 
    `type` int(11) not null, 
    PRIMARY KEY (`auditEventId`), 
    KEY `Transactions` (`clientId`,`eventTime`,`target1Id`,`type`), 
    KEY `TransactionsJoin` (`auditEventId`, `clientId`,`eventTime`,`target1Id`,`type`) 
) 

和(A版)的select

select ae.target1Id, ae.type, count(*) 
from AuditEvent ae 
where ae.clientId=4 
    and (ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00') 
group by ae.target1Id, ae.type; 

我結束了一個 '臨時使用' 和 '使用文件排序' 爲好。我嘗試刪除count(*)並使用select distinct代替,這不會導致'使用filesort'。如果有辦法返回join以獲得計數,這可能會沒問題。

最初決定跟蹤目標的target1Name和target2Name,因爲它們在創建審計記錄時存在。我也需要這些名字(最近會做)。

目前查詢(上面,缺少target1Name和target2Name列)在約2400萬條記錄中運行約5秒鐘。我們的目標是數以億計,我們希望查詢能夠繼續沿着這些方向發展(希望能夠保持在1-2分鐘之內,但我們希望更好),但是我的恐懼是一次我們擊中了大量的數據(不會模擬額外的行)。

我不確定獲得額外字段的最佳策略。如果我將這些列直接添加到select中,我會在查詢中失去「使用索引」。我嘗試了一個join回到表格,它保持'使用索引',但需要大約20秒。

我曾嘗試將eventTime列更改爲int而不是日期時間,但似乎並未影響索引使用或時間。

+0

什麼是您當前查詢時間和你有什麼瞭解下「很快」? – feeela

+0

對不起,添加那些細節 –

+0

你有clientId和eventTime的索引嗎?另外,請確認您是否有那些使用eventTime索引並且沒有進行全表掃描。 –

回答

3

正如你可能知道,這裏的問題是範圍條件ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00'它(因爲它總是這樣)打破Transactions指數的有效使用(即指數實際上是爲clientId方程的範圍條件第一部分和只用索引不用於分組)。

大多數情況下,解決方案是替換相等性檢查的範圍內的條件(在你的情況下,引入一個period列,組eventTime入週期和與period IN (1,2,3,4,5)替換BETWEEN子句)。但是這可能會成爲你桌子的開銷。

,你可以嘗試另一種解決方案是添加另一個索引(可能更換Transactions如果不再使用):(clientId, target1Id, type, eventTime),並使用以下查詢:

SELECT 
    ae.target1Id, 
    ae.type, 
    COUNT(
    NULLIF(ae.eventTime BETWEEN '2011-09-01 03:00:00' 
          AND '2012-09-30 23:57:00', 0) 
) as cnt, 
FROM AuditEvent ae 
WHERE ae.clientId=4 
GROUP BY ae.target1Id, ae.type; 

這樣的話,你會a)移動的範圍內的條件來結束,b)使用的分組的索引允許,C)使索引的覆蓋索引用於查詢(即查詢不需要磁盤IO操作)

UPD1: 對不起,yesteday我沒有仔細閱讀你的文章,沒有注意到你的問題是檢索target1Nametarget2Name。首先,我不確定你是否正確理解了Using index的含義。 Using index的缺席並不意味着查詢不使用索引,Using index意味着索引本身包含足夠的數據來執行子查詢(即索引正在覆蓋)。由於target1Nametarget2Name未包含在任何索引中,因此獲取它們的子查詢將沒有Using index

如果你的問題是多麼到這兩個字段添加到您的查詢(你認爲足夠快),那麼就請嘗試以下操作:

SELECT a1.target1Id, a1.type, cnt, target1Name, target2Name 
FROM (
    select ae.target1Id, ae.type, count(*) as cnt, MAX(auditEventId) as max_id 
    from AuditEvent ae 
    where ae.clientId=4 
     and (ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00') 
    group by ae.target1Id, ae.type) as a1 
JOIN AuditEvent a2 ON a1.max_id = a2.auditEventId 
; 
+0

兩者實際上都是有效答案;我期望通過不同的方式來提高查詢/獲取建議的性能,另外我想知道檢索非索引列的最佳方法。與我嘗試過的查詢相比,您的建議都有改進的性能! –

+0

@nickSpacek,好吧,我很高興它幫助=) – newtover