70

Java EE で MySql データベースを使用する Web プロジェクトに取り組んでいます。全体で 300 万行を超える 3 つのテーブルのデータを要約するビューが必要でした。各テーブルはインデックス付きで作成されました。しかし、[group by] で作成したビューからの条件付き select ステートメントの検索で、インデックスを利用する方法がわかりませんでした。

MySql でビューを使用するのは得策ではないという提案を人々から受けています。Oracleのようにmysqlでビューのインデックスを作成できないためです。しかし、私が取ったいくつかのテストでは、ビューの選択ステートメントでインデックスを使用できます。たぶん、私はそれらのビューを間違った方法で作成しました。

私の問題を説明するために例を使用します。

NBA ゲームのハイスコアのデータを記録するテーブルがあり、列 [hapend_in] にインデックスがあります。

CREATE  TABLE `highscores` (
   `tbl_id` int(11) NOT NULL auto_increment,
   `happened_in` int(4) default NULL,
   `player` int(3) default NULL,
   `score` int(3) default NULL,
   PRIMARY KEY  (`tbl_id`),
   KEY `index_happened_in` (`happened_in`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

データを挿入(8行)

INSERT INTO highscores(happened_in, player, score)
VALUES (2006, 24, 61),(2006, 24, 44),(2006, 24, 81),
(1998, 23, 51),(1997, 23, 46),(2006, 3, 55),(2007, 24, 34), (2008, 24, 37);

次に、コービー・ブライアントが毎年獲得した最高得点を表示するビューを作成します

CREATE OR REPLACE VIEW v_kobe_highScores
AS
   SELECT player, max(score) AS highest_score, happened_in
   FROM highscores
   WHERE player = 24
   GROUP BY happened_in;

コービー2006 年に獲得した最高得点を確認するための条件文を書きました。

select * from v_kobe_highscores where happened_in = 2006;

mysqlのヒキガエルで説明すると、mysqlはすべての行をスキャンしてビューを形成し、[happened_in]のインデックスを使用せずに、条件付きのデータを見つけることがわかりました。

explain select * from v_kobe_highscores where happened_in = 2006;

結果を説明する

私たちのプロジェクトで使用するビューは、数百万行のテーブル間で構築されています。ビューのデータ取得ごとにテーブルからすべての行をスキャンすることは受け入れられません。助けてください!ありがとう!

@zerkmsこれが私が実際にテストした結果です。の間に大きな違いは見られません。@ spencer7593 の指摘は正しいと思います。MySQL オプティマイザは、ビュー クエリでその述語を「プッシュ」しません。 実際のテスト

4

3 に答える 3

59

MySQL でビュー クエリにインデックスを使用するにはどうすればよいですか? 簡単な答えは、MySQL が使用できるインデックスを提供することです。

この場合、最適なインデックスは「カバリング」インデックスである可能性があります。

... ON highscores (player, happened_in, score)

MySQL はそのインデックスを使用する可能性が高く、EXPLAIN は次のように表示します: (インデックスの先頭の列の等式述語"Using index"により、 (インデックスの 2 番目の列) により、MySQL はインデックスを使用してそれを最適化できる場合があります。並べ替え操作を回避します。インデックスに列を含めると、インデックスによって参照されるデータ ページにアクセス (ルックアップ) することなく、クエリをインデックスから完全に満たすことができます。WHERE player = 24GROUP BY happened_idscore

それが素早い答えです。より長い答えは、MySQL がhappened_idビュー クエリの先行列を持つインデックスを使用する可能性は非常に低いということです。


ビューがパフォーマンスの問題を引き起こす理由

MySQL ビューで発生する問題の 1 つは、MySQL が述語を外部クエリからビュー クエリに「プッシュ」しないことです。

外側のクエリは を指定しますWHERE happened_in = 2006。MySQL オプティマイザーは、内部の「ビュー クエリ」を実行するときに述語を考慮しません。ビューのそのクエリは、外側のクエリの前に個別に実行されます。そのクエリの実行による結果セットは「実体化」されます。つまり、結果は中間の MyISAM テーブルとして保存されます。(MysQL が実行する操作を理解すれば、MySQL はそれを「派生テーブル」と呼びます。MysQL が使用する名前は理にかなっています。)

happened_in要するに、ビュー定義を形成するクエリを実行するときに、定義したインデックスがMySQL によって使用されていないということです。

中間の「派生テーブル」が作成された後、その「派生テーブル」を行ソースとして使用して、外側のクエリが実行されます。happened_in = 2006述語が評価されるのは、その外側のクエリが実行されるときです。

ビュー クエリのすべての行が格納されていることに注意してください。これは、(あなたの場合) の EVERY 値の行でありhappened_in、外側のクエリで等値述語を指定した行だけではありません。

ビュー クエリが処理される方法は、一部の人にとっては「予期しない」ものである可能性があります。これが、ビュー クエリが他のリレーショナル データベースで処理される方法と比較して、MySQL で「ビュー」を使用するとパフォーマンスの問題につながる可能性がある理由の 1 つです。


適切なカバリング インデックスによるビュー クエリのパフォーマンスの向上

ビューの定義とクエリを考えると、ビュー クエリの "インデックスを使用する" アクセス方法が最適です。それを取得するには、カバリング インデックスが必要です。

... ON highscores (player, happened_in, score).

これは、既存のビュー定義と既存のクエリにとって (パフォーマンスに関して) 最も有益なインデックスになる可能性があります。ビュー クエリでその列に等値述語があるため、このplayer列が先頭の列になります。次は列です。そのhappened_in列には GROUP BY 操作があり、MySQL はこのインデックスを使用して GROUP BY 操作を最適化できるようになるためです。scoreクエリで参照される他の唯一の列であるため、列も含めます。これにより、インデックスは「カバリング」インデックスになります。MySQL は、基になるテーブルのページにアクセスする必要なく、インデックス ページから直接そのクエリを満たすことができるからです。そして、それはそのクエリプランから抜け出すのと同じくらい良いです:「ファイルソートの使用」なしで「インデックスの使用」。


派生テーブルを使用しないスタンドアロン クエリとパフォーマンスを比較する

クエリの実行プランをビューと同等のスタンドアロン クエリと比較できます。

SELECT player
     , MAX(score) AS highest_score
     , happened_in
 FROM highscores
WHERE player = 24
  AND happened_in = 2006
GROUP
   BY player
    , happened_in

スタンドアロン クエリは、カバリング インデックスを利用することもできます。

... ON highscores (player, happened_in, score)

ただし、中間の MyISAM テーブルを具体化する必要はありません。


以前のいずれかが、あなたが求めていた質問に対する直接的な回答を提供しているかどうかはわかりません.

Q: MySQL でビュー クエリに INDEX を使用するにはどうすればよいですか?

A: ビュー クエリが使用できる適切な INDEX を定義します。

簡単な答えは、「カバリング インデックス」を提供することです (インデックスには、ビュー クエリで参照されるすべての列が含まれます)。そのインデックスの先頭の列は、等値述語で参照される列である必要があります (この場合、クエリに述語playerがあるため、列は先頭の列player = 24になります。また、GROUP BY で参照される列は先頭の列である必要があります)これにより、MySQL はGROUP BY、ソート操作ではなくインデックスを使用して操作を最適化できます。

ここで重要な点は、ビュー クエリは基本的にスタンドアロン クエリであるということです。そのクエリの結果は、中間の「派生」テーブル (ビューに対するクエリが実行されたときに作成される MyISAM テーブル) に格納されます。

MySQL でビューを使用することは必ずしも「悪い考え」ではありませんが、MySQL 内でビューを使用することを選択した人は、MySQL がそれらのビューを参照するクエリをどのように処理するかを認識するよう強く注意してください。また、MySQL がビュー クエリを処理する方法は、ビュー クエリが他のデータベース (Oracle、SQL Server など) によって処理される方法と (大幅に) 異なります。

于 2012-12-19T03:27:22.300 に答える
2

この場合、(この特定の順序で) 列を使用して複合インデックスを作成するのが最善の方法です。player + happened_in

PS: このような少量の行で mysql オプティマイザーの動作をテストしないでください。インデックスよりもフルスキャンが優先される可能性が高いためです。実生活で何が起こるかを見たい場合は、実生活と同じ量のデータを入力してください。

于 2012-12-19T03:11:15.003 に答える