9

これは「私のために宿題をしてください」のような質問のように感じますが、多くの行を持つテーブルに対してこのクエリをすばやく実行しようとして、ここで立ち往生しています。これは、スキーマを示す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 列が欠落している) は、約 2,400 万件のレコードに対して約 5 秒で実行されます。私たちの目標は数億であり、クエリがそれらの線に沿って引き続き実行されることを望んでいます(1〜2分未満に抑えることを望んでいますが、それをはるかに改善したいと考えています)が、私の懸念は一度ですヒットしない大量のデータにヒットします (追加の行をシミュレートする作業が進行中です)。

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

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

4

1 に答える 1

5

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

ほとんどの場合、解決策は範囲条件を等価チェックに置き換えることです(あなたの場合、period列を導入し、eventTimeピリオドにグループ化し、BETWEEN句を aに置き換えます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 index、インデックス自体にサブクエリを実行するのに十分なデータが含まれている (つまり、インデックスがカバーしている) ことを意味します。target1Nameとはどのインデックスにも含まれていないため、target2Nameそれらを取得するサブクエリにはUsing index.

これらの 2 つのフィールドをクエリに追加する方法だけを質問する場合 (十分に高速であると考えられます)、次のことを試してください。

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
;
于 2012-10-23T19:22:16.910 に答える