簡體   English   中英

優化具有數億行的表的查詢

[英]Optimize query for table with hundreds of millions of rows

這聽起來像是“為我做功課”之類的問題,但是我真的很難解決,試圖使該查詢針對具有許多行的表快速運行。 這是一個顯示模式的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`)
)

和(一個版本) 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 ,這不會導致“使用文件排序”。 如果有一種方法可以重新join來進行計數,那可能就可以了。

最初,決定跟蹤創建審計記錄時存在的目標的target1Name和target2Name。 我也需要這些名稱(最新名稱)。

當前,查詢(上面,缺少target1Name和target2Name列)在大約2400萬條記錄上運行約5秒鍾。 我們的目標是億萬用戶,我們希望查詢繼續按照這些原則執行(希望將查詢保持在1-2分鍾以內,但我們希望使其更好),但是我擔心的是我們擊中了大量數據(不會進行其他模擬行的工作)。

我不確定獲取更多字段的最佳策略。 如果將列直接添加到select ,則會丟失查詢中的“使用索引”。 我嘗試了join回表,該表保留了“使用索引”,但大約需要20秒。

我確實嘗試將eventTime列更改為int而不是datetime,但這似乎並未影響索引的使用或時間。

如您所知,這里的問題是ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00'的范圍條件ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00'ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00' Transactions索引的使用情況(即索引實際上僅用於clientId公式和范圍條件的第一部分,並且該索引不用於分組)。

最常見的解決方案是用相等性檢查替換范圍條件(在您的情況下,將一個period列,將eventTime到句點中,並將BETWEEN子句替換為period IN (1,2,3,4,5) ) 。 但這可能會成為您表的開銷。

您可能嘗試的另一種解決方案是添加另一個索引(如果不再使用它,則可能替換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:很抱歉,昨天我沒有仔細閱讀您的文章,也沒有注意到您的問題是檢索target1Nametarget2Name 首先,我不確定您是否正確理解Using index的含義。 沒有Using index並不意味着查詢不Using indexUsing 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
;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM