5

以下のステートメントのような select ステートメントがある場合、どの順序で、どの列をインデックスに含める必要がありますか?

SELECT MIN(BenchmarkID),
       MIN(BenchmarkDateTime),
       Currency1,
       Currency2,
       BenchmarkType
FROM Benchmark
       INNER JOIN MyCurrencyPairs ON Currency1 = Pair1
                            AND Currency2 = Pair2
WHERE BenchmarkDateTime > IN_BeginningTime
GROUP BY Currency1, Currency2, BenchmarkType;

注意事項:

  • Benchmark テーブルには数十億の行があります
  • MyCurrencyPairs テーブルは、10 レコード未満のローカル テーブルです。
  • IN_BeginningTime は入力パラメータです
  • 列 Currency1 と Currency2 は VARCHAR です
  • 列 BenchmarkID と BenchmarkType は INT です
  • 列 BenchmarkDateTime は datetime です (それが明らかであることを願っています)

Currency1、Currency2、BenchmarkType、BenchmarkDateTime、および BenchmarkID でインデックスを作成しましたが、必要な速度が得られませんでした。より良いインデックスを作成できますか?


編集 #1: 誰かが以下の結果の説明を要求しました。他に何か必要なことがあれば教えてください

ここに画像の説明を入力


編集 #2: 誰かが 2 つのテーブルの DDL を要求しました (これは create ステートメントであると想定しています)。

(このベンチマーク テーブルはデータベースに存在します)

CREATE TABLE `benchmark` (
    `SequenceNumber` INT(11) NOT NULL,
    `BenchmarkType` TINYINT(3) UNSIGNED NOT NULL,
    `BenchmarkDateTime` DATETIME NOT NULL,
    `Identifier` CHAR(6) NOT NULL,
    `Currency1` CHAR(3) NULL DEFAULT NULL,
    `Currency2` CHAR(3) NULL DEFAULT NULL,
    `AvgBMBid` DECIMAL(18,9) NOT NULL,
    `AvgBMOffer` DECIMAL(18,9) NOT NULL,
    `AvgBMMid` DECIMAL(18,9) NOT NULL,
    `MedianBMBid` DECIMAL(18,9) NOT NULL,
    `MedianBMOffer` DECIMAL(18,9) NOT NULL,
    `OpenBMBid` DECIMAL(18,9) NOT NULL,
    `ClosingBMBid` DECIMAL(18,9) NOT NULL,
    `ClosingBMOffer` DECIMAL(18,9) NOT NULL,
    `ClosingBMMid` DECIMAL(18,9) NOT NULL,
    `LowBMBid` DECIMAL(18,9) NOT NULL,
    `HighBMOffer` DECIMAL(18,9) NOT NULL,
    `BMRange` DECIMAL(18,9) NOT NULL,
    `BenchmarkId` INT(11) NOT NULL AUTO_INCREMENT,
    PRIMARY KEY (`BenchmarkId`),
    INDEX `NextBenchmarkIndex01` (`Currency1`, `Currency2`, `BenchmarkType`),
    INDEX `NextBenchmarkIndex02` (`BenchmarkDateTime`, `Currency1`, `Currency2`, `BenchmarkType`, `BenchmarkId`),
    INDEX `BenchmarkOptimization` (`BenchmarkType`, `BenchmarkDateTime`, `Currency1`, `Currency2`)
)

(ルーチンで MyCurrencyPairs テーブルを作成しています)

CREATE TEMPORARY TABLE MyCurrencyPairs
    (
        Pair1 VARCHAR(50),
        Pair2 VARCHAR(50)
    ) ENGINE=memory;
  CREATE INDEX IDX_MyCurrencyPairs ON MyCurrencyPairs (Pair1, Pair2);
4

2 に答える 2

1

BenchMarkDateTimeは、インデックスの最初の列である必要があります。

ルールは、複合インデックスの一部のみを使用する場合、使用される部分が先頭部分である必要があるということです。

次に、GroupByはインデックスと一致する必要があります。

範囲チェッククエリである「">」の代わりに「=」をクエリに使用させる方法があれば、パフォーマンスは向上します。

于 2012-07-23T17:23:52.590 に答える
0

主な問題は、MySQLがインデックスを直接使用して集計を処理できないことです。これは、との結合と、範囲条件がでMyCurrencyPairsあるときに要求しているという事実によるものです。より良い実行計画を得るには、これら2つを削除する必要があります。MIN(BenchmarkId)BenchmarkDateTime

まず、必要なインデックスと結果のクエリを見てみましょう。

ALTER TABLE benchmark
  ADD KEY `IDX1` (
    `Currency1`,
    `Currency2`,
    `BenchmarkType`,
    `BenchmarkDateTime`
  ),
  ADD KEY `IDX2` (
    `Currency1`,
    `Currency2`,
    `BenchmarkType`,
    `BenchmarkId`,
    `BenchmarkDateTime`
  );

SELECT
  (
    SELECT
      BenchmarkId
    FROM
      benchmark FORCE KEY (IDX2)
    WHERE
      Currency1 = ob.Currency1 AND
      Currency2 = ob.Currency2 AND
      BenchmarkType = ob.BenchmarkType
      AND BenchmarkDateTime > IN_BeginningTime
    ORDER BY
      Currency1, Currency2, BenchmarkType, BenchmarkId
    LIMIT 1
  ) AS BenchmarkId
  ob.*
FROM
  (
    SELECT
      MIN(BenchmarkDateTime),
      Currency1,
      Currency2,
      BenchmarkType
    FROM
      benchmark
    WHERE
      BenchmarkDateTime > IN_BeginningTime
    GROUP BY
      Currency1, Currency2, BenchmarkType
  ) AS ob
INNER JOIN
  MyCurrencyPairs ON Currency1 = Pair1 AND Currency2 = Pair2;

最初の変更は、GROUP BYパーツが独自のサブクエリで発生することです。これは、にCurrency1, Currency2, BenchmarkType表示されないものも含め、すべての組み合わせを生成することを意味しMyCurrencyPairsますが、多くの組み合わせがない限り、MySQLがインデックスを使用して操作を実行できるようになったため、これが高速になります。このサブクエリは、一時テーブルやファイルソートを必要とせずにIDX1を使用します。

2番目の変更は、MIN(BenchmarkId)パーツを独自のサブクエリに分離することです。そのサブクエリでの並べ替えはIDX2を使用して処理できるため、ここでも並べ替えは必要ありません。MySQLオプティマイザが正しいことを行うには、ヒントと「固定値」列でさえ-partにFORCE KEY (IDX2)表示Currency1Currency2れるBenchmarkType必要があります。ORDER繰り返しますが、これはトレードオフです。最終結果セットが大きい場合、サブクエリは損失になる可能性がありますが、行はそれほど多くないと思います。

そのクエリを説明すると、次のクエリプランが得られます(読みやすくするために、興味のない列は削除されています)。

+----+--------------------+-----------------+-------+---------+------+---------------------------------------+
| id | select_type        | table           | type  | key_len | rows | Extra                                 |
+----+--------------------+-----------------+-------+---------+------+---------------------------------------+
|  1 | PRIMARY            | <derived3>      | ALL   | NULL    | 1809 |                                       |
|  1 | PRIMARY            | MyCurrencyPairs | ref   | 106     |    2 | Using where                           |
|  3 | DERIVED            | benchmark       | range | 17      | 1225 | Using where; Using index for group-by |
|  2 | DEPENDENT SUBQUERY | benchmark       | ref   | 9       |  520 | Using where; Using index              |
+----+--------------------+-----------------+-------+---------+------+---------------------------------------+

すべての興味深い部分がインデックスで適切にカバーされており、一時テーブルもファイルソートも必要ないことがわかります。

テストデータのタイミングでは、このバージョンの速度は約20倍(1.07秒対0.05秒)であることが示されていますが、ベンチマークテーブルには約120万行しかなく、データ分散はかなり離れている可能性が高いため、YMMVです。

于 2012-07-24T21:18:37.630 に答える