2012-10-23 16 views
8

これは「私の宿題を私のために」するような感じですが、ここでは多くの行がある表に対してこの問合せをすばやく実行しようとしています。 Here's a SQLFiddle(多かれ少なかれ)スキーマを示しています。何億もの行を持つ表の問合せを最適化する

私は索引と一緒に試してみましたが、必要なすべての列が表示されますが、それほど成功していないものを取得しようとしました。

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:ここcreate

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; 

私も 'を使用し、一時的な' と '使用filesortレコード' で終わります。 count(*)を削除し、代わりにselect distinctを使用しましたが、これは 'Using filesort'を引き起こしませんでした。カウントを得るために戻ってjoinへの道があればこれはおそらく大丈夫でしょう。

元々、監査レコードの作成時に存在していたターゲットのtarget1Nameとtarget2Nameを追跡することが決定されました。私はそれらの名前も必要です(最新のものがあります)。

現在、上記のクエリ(上記のtarget1Nameおよびtarget2Name列がない)は、約2400万レコードで約5秒で実行されます。私たちの目標は数億に過ぎず、これらの行に沿ってクエリを実行し続けたいと思います(1-2分以内にしたいと思っていますが、もっとうまくやっていきたいですが)私たちはそれほど大量のデータを打たなかった(追加の行をシミュレートする作業が進行中)。

私は、追加のフィールドを取得するための最良の戦略についてはよく分かりません。 selectに列を直接追加すると、クエリで「インデックスの使用」が失われます。私はjoinをテーブルに戻してみました。これは 'Using index'を保持していますが、約20秒かかっています。

eventTime列をdatetimeではなくintに変更しようとしましたが、インデックスの使用や時間に影響していないようです。

+0

あなたの現在のクエリのタイミングと何が下に理解しては何ですか"早く"? – feeela

+0

申し訳ありません、それらの詳細を追加しました –

+0

clientIdとeventTimeにインデックスがありますか?また、eventTime索引を使用しており、フル・テーブル・スキャンを行っていないものがあるかどうかを確認してください。 –

答えて

3

あなたはおそらく理解しているように、ここでの問題は、Transactionsインデックス(つまり、インデックスは実際にのみclientId方程式と範囲条件の最初の部分のために使用されているとの効率的な使用を壊す(それは常にそうであるよう)範囲条件ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00'ですインデックスはグループ化に使用されません)。

はほとんどの場合、解決策は、平等性チェック(あなたのケースでは、期間にperiod列、グループeventTimeを導入し、period IN (1,2,3,4,5)BETWEEN句を置き換える)と範囲条件を置き換えることです。しかし、これはあなたのテーブルのオーバーヘッドになる可能性があります。(clientId, target1Id, type, eventTime)、そして次のクエリを使用します:あなたは試してみてください

別の解決策は、別の索引を(それはもはや使用されていない場合は、おそらくTransactionsを置き換える)を追加することでその方法を

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)指数がクエリのインデックスを覆う(すなわち、クエリでは、ディスク入出力操作を必要としない作る)

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、ok、私は助けてくれてうれしいです=) – newtover

関連する問題