公式ウェブサイトのドキュメント
MySQL :: MySQL 5.7 リファレンスマニュアル :: 8.3 最適化とインデックス
Mysql の最適化 (公式ドキュメントより) - 第 8 回 (インデックス最適化シリーズ)
目次
最適化とインデックス
インデックスを正しく作成するとクエリの速度が向上することがよくありますが、不必要なインデックスはスペースを浪費するだけで、挿入、更新、削除のオーバーヘッドが増加することがよくあります。これは、これらの操作で同時にインデックスを更新する必要があるためです。
ただし、インデックスは万能薬ではないため、次のシナリオではインデックスはあまり役に立ちません。
- 小さなテーブルまたは大きなテーブルですが、すべての行をリクエストする必要があります
- リクエストがテーブル内のほとんどの行を必要とする場合、連続読み取りによりディスクのシーク時間が短縮されるため、インデックス作成よりも連続読み取りの方が効率的であることがよくあります。
1 外部キーの最適化
テーブル内の複数の列を頻繁に読み取る場合は、アクセスの最も少ない列を小さなテーブルとして分離し、外部キーを使用してメイン テーブルに関連付け、可能な限りデータを読み取ることができるようにします。 ディスク I/O 読み取りを削減します。と書いています。
2列のインデックス
-
インデックスプレフィックス
Mysql では、次の例のように、一部のフィールドの一部のみをインデックスとして使用できます。インデックスには、すべての
TEXT
およびBLOB
フィールドが含まれます。CREATE TABLE test (blob_col BLOB, INDEX(blob_col(10)));
CHAR, VARCHAR, TEXT
型フィールドの場合、長さは文字数を意味しますが、フィールドなどの非文字列フィールドの場合BINARY, VARBINARY, BLOB
、長さはバイト数を意味することに注意してください。 -
フルテキストインデックス
このインデックスはCHAR、 VARCHAR、および TEXT列に適用できます。また、フルテキスト インデックスはプレフィックスをサポートせず、フルカラム インデックスのみをサポートします。全文インデックス作成は、クエリが次の特性を満たす場合に役立ちます。
FULLTEXT
クエリ ステートメントの値には、document ID
次のいずれかが必要ですsearch rank
(グレーディング??)FULLTEXT
クエリ ステートメントには、クエリ結果が降順で並べ替えられ、LIMIT
ステートメント選択top N
項目があります。この場合、最適化を使用するには、クエリ ステートメントに列が 1 つだけ含まれてWHERE
いないことを確認する必要があります。ORDER BY
FULLTEXT
クエリはCOUNT(*)
結果を取得するだけでステートメントはありません。フィルタリングがWHERE
必要な場合はと記述し、比較演算子はありません。WHERE
WHERE
WHERE MATCH(TEXT) AGAINST('other_text')
> 0
-
MEMORYストレージエンジンのインデックス
インメモリ ストレージ エンジンの場合、
HASH
デフォルトでインデックスが使用されますが、インデックスもサポートされていますBTREE
。
3 列インデックスと複数列インデックス
Mysql は単一のカラムをインデックスとしてサポートしており、複数のカラムをインデックスとして使用することもできます。複数のカラムを使用する場合、左端 (インデックスの一番左のプレフィックス) のカラムをクエリに使用すると、インデックスを使用できます。それ以外の場合、Mysql はインデックスを使用することはできません。例:
テーブルに次のインデックスが含まれているとします。
INDEX name (last_name,first_name)
名前インデックスは、次のクエリ ステートメントに使用できます。
SELECT * FROM test WHERE last_name='Jones';
SELECT * FROM test
WHERE last_name='Jones'
AND (first_name='John' OR first_name='Jon');
SELECT * FROM test
WHERE last_name='Jones'
AND first_name >='M' AND first_name < 'N';
次のステートメントでは、クエリに左端の列が使用されないため、インデックスを使用できません。
SELECT * FROM test WHERE first_name='John';
SELECT * FROM test
WHERE last_name='Jones' OR first_name='John';
col1
2 つの列とがあると仮定しcol2
、インデックスがcol1
と に基づいて構築されているだけであれcol2
ば、インデックスを直接使用できますが、合計が別個のインデックス (つまり、複数列インデックスではない) である場合col1
、col2
Mysql は使用しようとします。 (セクション8.2.1.3「インデックス マージの最適化」Index Merge optimization
を参照 ) テクノロジ、または 2 つのインデックスのどちらが最も厳しいかを判断する条件により、どのインデックスを使用するかを決定する前に、できるだけ多くの行を除外できます。
4 B ツリー インデックスとハッシュ インデックスの比較
-
B ツリー インデックスの特性
B ツリー インデックスは =、 >、 >=、 <、 <=、または BETWEEN演算子をサポートしており、演算子もサポートしていますが、フィールドがワイルドカードで始まっていないこと
LIKE
を確認する必要があります。LIKE
インデックスがすべてをカバーしていない場合
AND
、Mysql はインデックスを使用できません。つまり、インデックスを使用できるようにするには、インデックスのプレフィックス (左端の部分) が必ずそれぞれAND
、たとえば次のようになります。... WHERE index_part1=1 AND index_part2=2 AND other_column=3 /* index = 1 OR index = 2 */ ... WHERE index=1 OR A=10 AND index=2 /* optimized like "index_part1='hello'" */ ... WHERE index_part1='hello' AND index_part3=5 /* Can use index on index1 but not on index2 or index3 */ ... WHERE index1=1 AND index2=2 OR index1=3 AND index3=3;
次のステートメントではインデックスは使用されません。
/* index_part1 is not used */ ... WHERE index_part2=1 AND index_part3=2 /* Index is not used in both parts of the WHERE clause */ ... WHERE index=1 OR A=10 /* No index spans all rows */ ... WHERE index_part1=1 OR index_part2=10
(ここがよくわかりませんか?)
注意しなければならないことは次のとおりです。
Mysql は常にインデックスの使用を選択しません。大量の行を読み取る必要がある場合、インデックスによりシーク消費が増加するため、インデックスを使用する効率がインデックスの効率よりも低くなることがあります
table scan
。連続読み取りにより、ディスクtable scan
I/O をより効率的に使用できます。 -
ハッシュインデックスの特性
Hash
ツリー インデックスと比較するとB
、インデックスには次のような違いがある場合があります。Hash
=
インデックスはOR演算子にのみ使用でき<=>
、範囲クエリには使用できません。- オプティマイザはインデックスを使用して操作を
Hash
高速化することはできませんORDER BY
Mysql
2 つの行の間にvalue
何行 (BETWEEN
ステートメント)があるかを評価できません- 完全なインデックスのみが正しく機能しますが、
BTREE
そうではなく、使用してくださいleftmost prefix key
5 インデックス拡張の使用
InnoDB は、次の表のように、主キーを新しいインデックスとしてセカンダリ インデックスに自動的に追加します。これは内部的に行われ、ユーザーが認識することはできません。
CREATE TABLE t1 (
i1 INT NOT NULL DEFAULT 0,
i2 INT NOT NULL DEFAULT 0,
d DATE DEFAULT NULL,
PRIMARY KEY (i1, i2),
INDEX k_d (d)
) ENGINE = InnoDB;
このとき、インデックス k_d の内部実際の実装方法は次のようになります: (d, i1, i2)
つまり、主キー (i1, i2) が k_d の後ろに自動的に追加され、新しい二次インデックスが形成されます。
ref
この最適化方法は、 、 、およびのrange
インデックスindex_merge
アクセス シナリオで使用できます。Loose Index Scan
join
sort
MIN()/MAX()。
たとえば、次のステートメント:
SELECT COUNT(*) FROM t1 WHERE i1 = 3 AND d = '2000-01-01'
このとき、インデックスは内部的i1
に追加されるため、このステートメントはそのようなインデックス (インデックスの左端のプレフィックスです)k_d
を使用できますが、使用される列が増えるため、スキャンする行数が大幅に削減されます。(d, i1)
(d, i1)
(d, i1, i2)
6 目に見えないインデックス
この種類のインデックスは実際に存在するインデックスを参照しますが、オプティマイザはそれを使用しません。非 にのみ設定できますprimary key
。他の種類のインデックスは への設定をサポートしますINVISIBLE
。
たとえば次の例です。
CREATE TABLE t1 (
i INT,
j INT,
k INT,
INDEX i_idx (i) INVISIBLE
) ENGINE = InnoDB;
CREATE INDEX j_idx ON t1 (j) INVISIBLE;
ALTER TABLE t1 ADD INDEX k_idx (k) INVISIBLE;
この種のインデックスの一般的な目的: インデックスの削除がパフォーマンスに及ぼす影響をテストすること。この種のインデックスは実際には削除されませんが、オプティマイザはインデックスを考慮しなくなるため、テーブルには大きな影響を与えません。
インデックスはInvisible
有効ですが、このインデックスは通常のインデックスと同じ完全な機能を備えていることに注意してください。つまり、Mysql は追加、削除、変更時にも更新されます。同様に、非常に大きなテーブルに作成されたInvisible Index
場合Invisible Index
、通常のインデックスと同じになりますが、コストも非常に膨大になります。
7 降順インデックス
Mysql は降順インデックスをサポートしています。以前のバージョンとは異なり、インデックス定義内の DESC は無視されなくなり、インデックスの内部保存方法も降順で採用されました。それ以前は、Mysql はインデックスを逆順にスキャンする方法を使用していました。現在、降順の効率は以前よりもはるかに高くなっています。
降順インデックスは主に次のシナリオで使用されます。
- 降順インデックスは InnoDB エンジンのみをサポートし、次の制限があります。
- セカンダリ インデックスに降順キーが含まれている場合、またはプライマリ キーに降順キーが含まれている場合、Mysql はバッファリングの変更をサポートできません。
- InnoDB の SQL パーサーは降順インデックスを使用しません。フルテキスト クエリの場合、これは、すでにインデックスが作成されているテーブルの列を
FTS_DOC_ID
降順インデックスとして定義できないことを意味します。
- 降順キーを持つインデックスは、特に GROUP BY を使用しない集計関数を含むステートメントの場合、MIN/MAX では最適化できません。
- 降順インデックスは BTREE のみをサポートし、HASH インデックスはサポートしません。降順インデックスは FULLTEXT インデックスや SPATIAL インデックスをサポートできません。