MySQLインデックス-ベストプラクティスは何ですか?


208

私はMySQLデータベースでしばらくインデックスを使用していますが、それらについて適切に学習したことはありません。一般に、WHERE句を使用して検索または選択するフィールドにインデックスを付けますが、白黒のように見えない場合があります。

MySQLインデックスのベストプラクティスは何ですか?

状況/ジレンマの例:

  • テーブルに6つの列があり、それらのすべてが検索可能である場合、それらのすべてにインデックスを付けるか、どれにもインデックスを付けないか?

  • インデックス作成のパフォーマンスへの悪影響は何ですか?

  • サイトの一部から検索できるVARCHAR 2500列がある場合、それをインデックスに登録する必要がありますか?


5
おそらく質問のタグを付け直す必要があります。インデックスの選択は、あらゆるデータベースモデルの最適化にとって重要な部分です。そして、私の見解では、phpとは無関係です。
VGE


回答:


242

あなたは間違いなくインデックス作成にある程度の時間を費やす必要があり、それについて多くのことが書かれており、何が起こっているのかを理解することが重要です。

大まかに言えば、インデックスはテーブルの行に順序付けを課します。

簡単にするために、テーブルが単なる大きなCSVファイルであると想像してください。行が挿入されるたびに、行の最後に挿入さます。したがって、テーブルの「自然な」順序は、行が挿入された順序にすぎません。

そのCSVファイルが非常に初歩的なスプレッドシートアプリケーションに読み込まれたとしましょう。このスプレッドシートでは、データを表示し、行に順番に番号を付けます。

ここで、3番目の列に値「M」を持つすべての行を見つける必要があると想像してください。利用可能なものを考えると、1つのオプションしかありません。各行の3番目の列の値をチェックしてテーブルをスキャンします。行が多い場合、この方法(「テーブルスキャン」)には時間がかかることがあります。

このテーブルに加えて、インデックスがあると想像してください。この特定のインデックスは、3番目の列の値のインデックスです。インデックスは、3番目の列のすべての値を意味のある順序(たとえば、アルファベット順)で一覧表示し、それぞれについて、その値が表示される行番号のリストを提供します。

これで、3番目の列の値が「M」であるすべての行を見つけるための優れた戦略ができました。たとえば、バイナリ検索を実行できます。テーブルスキャンではN行(Nは行数)を検索する必要がありますが、バイナリ検索では、最悪の場合、log-nインデックスエントリを確認するだけで済みます。うわー、それは確かにはるかに簡単です!

もちろん、このインデックスがあり、テーブルに行を追加している場合(最後に、概念的なテーブルが機能しているため)、毎回インデックスを更新する必要があります。したがって、新しい行を書き込んでいる間はもう少し作業が必要ですが、何かを検索しているときは時間を大幅に節約できます。

したがって、一般に、インデックス付けは読み取り効率と書き込み効率のトレードオフを作成します。インデックスがない場合、挿入は非常に高速になります。データベースエンジンはテーブルに行を追加するだけです。インデックスを追加すると、エンジンは挿入の実行中に各インデックスを更新する必要があります。

一方、読み取りははるかに速くなります。

うまくいけば、最初の2つの質問がカバーされます(他の人が答えたように、適切なバランスを見つける必要があります)。

3番目のシナリオはもう少し複雑です。LIKEを使用している場合、インデックスエンジンは通常、最初の「%」までの読み取り速度に役立ちます。つまり、WHERE列LIKE 'foo%bar%'をSELECTしている場合、データベースは、インデックスを使用して、列が「foo」で始まるすべての行を見つけ、その中間行セットをスキャンしてサブセットを見つける必要があります。 「バー」が含まれています。SELECT ... WHERE列LIKE '%bar%'はインデックスを使用できません。その理由をご理解いただければ幸いです。

最後に、複数の列のインデックスについて考える必要があります。概念は同じで、LIKEのものと同様に動作します。基本的に、(a、b、c)にインデックスがある場合、エンジンは可能な限り左から右にインデックスを使用し続けます。したがって、列aの検索では、(a、b)の場合と同様に、(a、b、c)インデックスを使用できます。ただし、WHERE b = 5 AND c = 1)を検索する場合、エンジンは全表スキャンを実行する必要があります。

うまくいけば、これは少し光を当てるのに役立ちますが、これらのことを詳細に説明する良い記事を探すために数時間かけて過ごすことをお勧めします。特定のデータベースサーバーのドキュメントを読むこともお勧めします。クエリプランナーがインデックスを実装して使用する方法は、かなり大きく異なります。


10
何についてのFULLTEXTインデックス?彼らは次のような状態を助けることができますLIKE '%bar%'か?
Septagram 2013年

2
@Septagram- が「単語」の場合FULLTEXTそのクエリに役立ちます。 任意の部分文字列ではなく単語を処理します(そうです)。 barFULLTEXTLIKE
リックジェームズ

@timdevは最初の質問にどの部分で答えられたかを明確にしていますか?私はあなたの貴重な答えの最初と2番目の部分(うまくいけば、あなたの最初の2つの質問をカバーする前と後で)で答えられた2番目と3番目の質問を検出できます
マヌエルジョーダン

1
@ManuelJordan-最初の質問に対する簡単な答えはありません。それは、予想される(またはさらに良い、観測された)使用状況のコンテキストでトレードオフをどのようにバランスさせたいかに依存します。
timdev

57

More Mastering the Art of Indexingのようなプレゼンテーションをご覧ください。

アップデート12/2012:私の新しいプレゼンテーションを投稿しました:How to Design Indexes、Really。2012年10月にサンタクララのZendConで、2012年12月にパーコナライブロンドンでこれを発表しました。

最適なインデックスの設計は、アプリで実行するクエリに一致する必要があるプロセスです。

インデックスを付けるのに最適な列、またはすべての列にインデックスを付けるか、列を含めないか、複数の列にまたがるインデックスにするかなどの汎用ルールを推奨することは困難です。実行する必要のあるクエリによって異なります。

はい、オーバーヘッドがあるため、不必要にインデックスを作成しないでください。しかし、あなたがする必要があり、あなたがすぐに実行する必要があるクエリに利益を与えるインデックスを作成します。インデックスのオーバーヘッドは通常、その利点よりはるかに重要です。

VARCHAR(2500)の列​​の場合、おそらくFULLTEXTインデックスまたはプレフィックスインデックスを使用する必要があります。

CREATE INDEX i ON SomeTable(longVarchar(100));

長いvarcharの途中にある可能性のある単語を検索する場合、従来のインデックスは役に立たないことに注意してください。そのためには、フルテキストインデックスを使用します。


3
どうもありがとうございます。slideshare.net/matsunobu/…は本当に役に立ちました。
Bishal Paudel、2015



1
素晴らしいプレゼンテーション(2012年のもの)は、インデックスの全体的なポイントを本当に理解しています。
DarkteK

46

他の回答では良いアドバイスの一部を繰り返しませんが、追加します:

複合インデックス

複数の列を含むインデックスである複合インデックスを作成できます。MySQLはこれらをから右に使用できます。だからあなたが持っている場合:

Table A
Id
Name
Category
Age
Description

Name / Category / Ageの順に含まれる複合インデックスがある場合、これらのWHERE句はインデックスを使用します。

WHERE Name='Eric' and Category='A'

WHERE Name='Eric' and Category='A' and Age > 18

だが

WHERE Category='A' and Age > 18

すべてを左から右に使用する必要があるため、このインデックスは使用しません。

説明する

Explain / Explain Extendedを使用して、MySQLで使用可能なインデックスと、実際に選択するインデックスを理解します。 MySQLはのみ使用しますONEクエリごとのキーを

EXPLAIN EXTENDED SELECT * from Table WHERE Something='ABC'

遅いクエリログ

スロークエリログをオンにして、どのクエリの実行が遅いかを確認します

ワイドカラム

幅の広い列があり、ほとんどの区別が最初の数文字で発生する場合、インデックスで使用できるのは最初のN文字のみです。例:varchar(255)として定義されたReferenceNumber列がありますが、97%の場合、参照番号は10文字以下です。最初の10文字のみを表示するようにインデックスを変更し、パフォーマンスをかなり改善しました。


最後の部分について質問があります。VARCHARを使用して列を作成する場合は、常に255に設定する必要があることをどこかで読みました。このタイプの列に設定されたインデックスは、最初の10文字のみを参照するように制限できると述べました。あなたはそれをどのように正確に行うことができますか?
AlexioVay 2017

20

テーブルに6つの列があり、それらすべてが検索可能である場合、それらすべてにインデックスを付けるか、どれにもインデックスを付けないでください

フィールドごとに検索していますか、それとも複数のフィールドを使用して検索していますか?どのフィールドが最も検索されていますか?フィールドの種類は何ですか?(たとえば、インデックスはVARCHARよりもINTでうまく機能します)実行されているクエリでEXPLAINを使用してみましたか?

インデックス作成のネガティブなパフォーマンスへの影響は何ですか

更新と挿入は遅くなります。余分なストレージスペースの要件もありますが、最近はそれほど重要ではありません。

私のサイトの一部から検索可能なVARCHAR 2500列がある場合、それをインデックス化する必要があります

いいえ、それがUNIQUE(つまり、すでにインデックスが付けられていることを意味します)またはそのフィールドで完全一致のみを検索する(LIKEまたはmySQLの全文検索を使用しない)場合を除きます。

一般に、WHERE句を使用して検索または選択するフィールドにインデックスを付けます

通常、最も照会されるフィールドにインデックスを付け、次にVARCHARSであるフィールドではなく、INT / BOOLEAN / ENUMにインデックスを付けます。多くの場合、個々のフィールドのインデックスではなく、結合されたフィールドのインデックスを作成する必要があることを忘れないでください。EXPLAINを使用し、遅いログを確認します。


11

データを効率的にロードする:インデックスを使用すると、検索の速度は上がりますが、挿入と削除の速度が低下します。また、インデックス付きの列の値の更新も遅くなります。つまり、インデックスは書き込みを伴うほとんどの操作を遅くします。これは、行の書き込みにはデータ行の書き込みだけでなく、インデックスの変更も必要になるために発生します。テーブルのインデックスが多いほど、必要な変更が多くなり、平均的なパフォーマンスの低下が大きくなります。ほとんどのテーブルは多くの読み取りと少ない書き込みを受信しますが、書き込みの割合が高いテーブルの場合、インデックスの更新のコストはかなり大きくなる可能性があります。

インデックスの回避:クエリのパフォーマンスを向上させるために特定のインデックスが必要ない場合は、作成しないでください。

ディスク容量:インデックスはディスク容量を使用し、複数のインデックスはそれに応じてより多くの容量を使用します。これにより、インデックスがない場合よりも早くテーブルサイズの制限に達する可能性があります。可能な限りインデックスを使用しないでください。

要点:索引を付けすぎないでください


5

一般に、インデックスはデータベース検索を高速化するのに役立ちますが、余分なディスク領域を使用し、INSERT/ UPDATE/ DELETEクエリが遅くなるという欠点があります。使用EXPLAINしてMySQLがあなたのインデックスを使用したときに見つけるために結果を読み取ります。

テーブルに6つの列があり、それらすべてが検索可能である場合、それらのすべてにインデックスを付けるか、どれにもインデックスを付けないか?

6つの列すべてにインデックスを付けることは、常にベストプラクティスとは限りません。

(a)特定の情報を検索するときに、これらの列のいずれかを使用しますか?

(b)それらの列の選択性はどのくらいですか(テーブルにあるレコードの総数と比較して、そこに格納されている個別の値の数)?

MySQLはコストベースのオプティマイザを使用します。これは、クエリを実行するときに「最も安い」パスを見つけようとします。また、選択性の低いフィールドは適切な候補ではありません。

インデックス作成のパフォーマンス低下の影響は何ですか?

すでに回答:余分なディスク容量、挿入時のパフォーマンス低下-更新-削除。

サイトの一部から検索可能なVARCHAR 2500列がある場合、それをインデックスに登録する必要がありますか?

FULLTEXTインデックスをお試しください。


4

1/2)インデックスは特定の選択操作を高速化しますが、挿入、更新、削除などの他の操作を遅くします。微妙なバランスになり得ます。

3)全文索引またはおそらくスフィンクスを使用します


防ぐためにslow down other operations like insert, update and deletes使用することができSTART TRANSACTION; YOUR CODE HERE; COMMIT ないよう助けることができるslowing down、それは一つだけを一度に制約をチェックするよう、他の操作を。CAVEAT:あなたが使用している場合はREPLACE INTO、あなたのSQL_MODE<> STRICT_ALL_TABLESOR に置き換え、挿入、重複を無視します。TRADITIONALBulk Load
JayRizzo 2017年

トランザクションは、すべてのMySQLエンジンでサポートされているわけではありません。AFAIK、トランザクションは暗黙的にのみ使用されている場合でも、DB操作を遅くします。実際のパフォーマンスに基づいて設計する必要があるのは、インデックスやトランザクションを含むさまざまな最適化の選択肢のプロファイル(パフォーマンスの測定)を半自動的に行う方法です。
David Spector
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.