はい、承知いたしました。MySQLのCREATE INDEX
の使い方と効果について、約5000語の詳細な解説記事を作成します。
SQL:MySQL CREATE INDEXの使い方と効果
1. はじめに:データベースパフォーマンスの生命線、インデックス
Webアプリケーション、モバイルサービス、業務システムなど、現代のソフトウェアは大量のデータを扱います。そして、そのデータはしばしばリレーショナルデータベース、特にMySQLに格納されています。データベースの応答速度は、ユーザー体験やシステム全体のパフォーマンスに直結するため、非常に重要です。
データベースのパフォーマンスを向上させるための最も基本的かつ効果的な手段の一つが「インデックス」の適切な利用です。インデックスは、データベーステーブル内のデータを効率的に検索、ソート、グループ化するための仕組みであり、SQLクエリの実行速度を劇的に改善することができます。
この記事では、MySQLにおけるインデックスの基本から応用までを網羅的に解説します。具体的には、インデックスとは何か、なぜ必要なのかといった基礎知識から、CREATE INDEX
文を使った様々なインデックスの作成方法、インデックスの種類ごとの特徴、インデックスがクエリに与える効果(メリットとデメリット)、そして効果的なインデックス設計のためのベストプラクティスまで、詳細に説明します。特に、インデックスの効果を検証するための必須ツールであるEXPLAIN
についても詳しく解説します。
この記事は、MySQLを使い始めたばかりの方、データベースのパフォーマンスに課題を感じている開発者やデータベース管理者(DBA)の方々を対象としています。インデックスを理解し、適切に使いこなすことで、あなたのアプリケーションの応答速度を向上させ、より良いユーザー体験を提供できるようになるでしょう。
さあ、データベースパフォーマンス改善の鍵となるインデックスの世界へ踏み込みましょう。
2. インデックスとは何か? アナロジーで理解する
インデックスとは、データベーステーブルの中から特定のデータ行を素早く探し出すための「目次」のようなものです。
アナロジー1:本の索引
分厚い技術書で特定のキーワードについて知りたいとき、本の最初からページをめくることはしませんよね。代わりに、巻末にある「索引(インデックス)」を開き、キーワードがどのページに記載されているかを確認してから、そのページに直接飛びます。これにより、目的の情報にたどり着くまでの時間を大幅に短縮できます。
データベースのインデックスもこれと同じ役割を果たします。インデックスは、テーブル内の特定カラムの値と、その値を持つデータ行がどこにあるか(物理的な位置、あるいは主キー値など)を紐付けて記録しています。クエリで特定の値を検索する際、データベースシステムはテーブル全体をスキャンするのではなく、まずインデックスを参照します。インデックスから目的の値を持つ行の場所を特定し、その行を直接読み込むことで、高速なデータアクセスを実現します。
アナロジー2:電話帳
昔ながらの電話帳を考えてみましょう。電話帳は名前のアルファベット順に並んでいます。特定の人の電話番号を探すとき、あなたはまず名前の頭文字から目的のページを開き、そこから名前を順に見ていきます。名前がきちんとソートされているため、目的の名前を見つけるのにそれほど時間はかかりません。
もし電話帳が全くソートされておらず、ランダムに名前と電話番号が記載されていたらどうなるでしょうか? 特定の人の電話番号を知るためには、電話帳の最初から最後まで全ての名前と番号を順番に見ていくしかありません。これは非常に時間がかかります。
データベーステーブルも同様に、デフォルトではデータが格納された順序や、物理的な格納場所に基づいてデータが並んでいます。特定の条件に一致する行を探すには、インデックスがない場合、テーブルの最初の行から最後の行まで全てを読み込んで条件に合うか確認する必要があります。これを「フルテーブルスキャン」と呼びます。データ量が少ないうちは問題になりませんが、テーブルが大きくなるにつれてフルテーブルスキャンのコストは無視できないほど大きくなり、クエリの実行速度を著しく低下させます。
インデックスは、指定したカラムの値に基づいてデータを「並べ替えられた状態」として表現(物理的に並べ替えるわけではありませんが、検索を効率化する構造で保持)することで、電話帳のように素早く目的のデータを見つけ出せるようにする仕組みなのです。
3. なぜインデックスが必要か? パフォーマンスへの貢献
インデックスが必要な理由は、主にデータベースに対する以下の操作のパフォーマンスを向上させるためです。
-
データ検索(SELECT)の高速化:
WHERE
句で特定の条件に合致する行を絞り込む際に、インデックスは最も大きな効果を発揮します。インデックスがあることで、フルテーブルスキャンを回避し、必要な行に直接アクセスできます。JOIN
句を使って複数のテーブルを結合する際、結合条件に使用されるカラムにインデックスがあると、結合処理が効率化され高速になります。ORDER BY
句で結果セットを特定のカラムでソートする際に、インデックスがソート順と一致していれば、別途ソート処理を行う必要がなくなる(またはソート処理の負荷が軽減される)ため、高速化されます。GROUP BY
句でデータをグループ化する際にも、インデックスが利用されることで効率が向上します。
-
データ更新(UPDATE, DELETE)の効率化:
UPDATE
文やDELETE
文は、内部的にはまず更新・削除対象の行を「検索」します。この検索部分でWHERE
句が使われるため、インデックスがあれば対象行の特定が速くなり、結果として更新・削除処理全体も高速化されます。
-
制約の実装:
PRIMARY KEY
やUNIQUE
制約を定義すると、MySQLは自動的にインデックスを作成します。これらのインデックスは、データの重複を防ぎ、データの一貫性を保つ役割も果たします。主キーはテーブル内の各行を一意に識別するためのインデックスであり、データの参照において最も頻繁に利用されます。
このように、インデックスはデータ量の増加に伴うパフォーマンス劣化を防ぎ、データベースシステム全体の応答性を維持・向上させるために不可欠な要素です。
4. MySQLのインデックスの種類
MySQLは、様々なデータ型や検索要件に対応するために、いくつかの異なるタイプのインデックスを提供しています。ストレージエンジン(InnoDBやMyISAMなど)によってサポートされるインデックスタイプや実装方法が異なりますが、最も一般的で重要なのはB-Treeインデックスです。
主要なインデックスタイプを以下に説明します。
4.1. B-Treeインデックス (BTREE)
- 特徴: MySQLで最も一般的かつデフォルトのインデックスタイプです。バランス木(Balanced Tree)というデータ構造に基づいており、検索、範囲検索、ソートなど、幅広い種類のクエリで高いパフォーマンスを発揮します。InnoDBおよびMyISAMストレージエンジンでサポートされます。
- どのように機能するか: B-Treeインデックスは、インデックス付けされたカラムの値を階層的なツリー構造で保持します。ツリーの各ノードはソートされたキーと、そのキーを持つデータへのポインタ(または主キー値)を含みます。検索時には、ツリーのルートノードから開始し、比較を行いながら適切な子ノードへと辿っていくことで、目的の値を持つデータ行を効率的に見つけ出します。
- 有効なクエリタイプ:
- 等価検索 (
WHERE col = value
) - 範囲検索 (
WHERE col > value
,WHERE col < value
,WHERE col BETWEEN value1 AND value2
) LIKE
を使った前方一致検索 (WHERE col LIKE 'prefix%'
)ORDER BY
句でのソートGROUP BY
句でのグループ化- 複数のカラムを組み合わせた複合インデックスの場合、「先頭一致」の条件を満たす検索(後述)
- 等価検索 (
4.2. ハッシュインデックス (HASH)
- 特徴: 値からハッシュ値を計算し、そのハッシュ値を使ってデータの物理アドレスを直接参照するインデックスです。主にMEMORYストレージエンジンで使用され、InnoDBではアダプティブハッシュインデックス(AHI)として内部的に利用されることがあります。MyISAMではサポートされていません。
- どのように機能するか: ハッシュ関数を用いてインデックス付けされたカラムの値からハッシュ値を計算し、そのハッシュ値をキーとしてハッシュテーブルに格納します。ハッシュテーブルのエントリは、元のカラム値と、その値を持つデータ行へのポインタを保持します。
- 有効なクエリタイプ:
- 等価検索 (
WHERE col = value
) のみに非常に高速です。
- 等価検索 (
- 制約:
- ハッシュ関数を使うため、範囲検索 (
>
,<
,BETWEEN
) やソート (ORDER BY
)、部分一致検索 (LIKE
) には利用できません。 - インデックスのデータ構造そのものがソートされていないためです。
- ハッシュの衝突が発生する可能性があります(異なる値が同じハッシュ値になる)。
- ハッシュ関数を使うため、範囲検索 (
4.3. R-Treeインデックス (RTREE)
- 特徴: 空間データ(GISデータ型、例:
POINT
,LINESTRING
,POLYGON
)のために設計されたインデックスです。SPATIALインデックスと呼ばれます。MyISAMおよびInnoDB(MySQL 5.7以降)でサポートされます。 - どのように機能するか: R-Treeは、空間オブジェクトを最小外接矩形(Minimum Bounding Rectangle, MBR)で表現し、階層構造で管理します。空間検索クエリ(例:ある領域に含まれるオブジェクトを探す)を実行する際に、効率的に検索範囲を絞り込むことができます。
- 有効なクエリタイプ:
MBRContains()
,MBRWithin()
,MBROverlaps()
などの空間検索関数を使ったクエリ。
4.4. 全文検索インデックス (FULLTEXT)
- 特徴: テキストカラム(
CHAR
,VARCHAR
,TEXT
など)内の単語を検索するために使用されます。通常のB-Treeインデックスが単語単位ではなくカラム値全体に対して機能するのに対し、FULLTEXTインデックスはテキストを解析して単語(トークン)に分解し、どのドキュメント(行)にどの単語が含まれているかを記録します。MyISAMおよびInnoDB(MySQL 5.6以降)でサポートされます。 - どのように機能するか: 転置インデックス(Inverted Index)と呼ばれるデータ構造を使用します。これは、単語からそれが出現するドキュメントリストへのマッピングを保持します。
- 有効なクエリタイプ:
MATCH() AGAINST()
構文を使った自然言語検索やBOOLEANモード検索。- キーワードに基づいた関連性の高いドキュメントの検索。
4.5. クラスタ化インデックス (Clustered Index) – 主キー
- 特徴: InnoDBストレージエンジンにおいて、テーブルの主キーに自動的に作成されるインデックスです。クラスタ化インデックスは、データ行そのものがインデックスのリーフノードに物理的に格納され、インデックス順にソートされた状態でディスク上に配置されます(ただし、物理的な格納順序は時間の経過とともに断片化する可能性があります)。テーブルごとに1つだけ存在できます。
- どのように機能するか: 主キー値を使って行を検索する場合、インデックスを辿って最終的にリーフノードに到達した時点で、必要なデータ行全体を取得できます。
- 効果: 主キーによる検索が非常に高速です。また、主キーの範囲検索も効率的です。
- 重要な点: InnoDBのセカンダリインデックスは、リーフノードにデータ行への物理的なポインタではなく、対応する行の「主キー値」を格納します。したがって、セカンダリインデックスを使って検索する場合、まずセカンダリインデックスを辿って主キー値を取得し、次にその主キー値を使ってクラスタ化インデックスを辿ってデータ行を取得するという「二段階」のアクセスが必要になります(これを「Lookup」と呼びます)。このため、主キーによる検索はセカンダリインデックスによる検索よりも一般的に高速です。主キーがないInnoDBテーブルの場合、MySQLは非表示の仮想クラスタ化インデックスを生成します。
4.6. セカンダリインデックス (Secondary Index)
- 特徴: 主キー以外のカラムに作成されるインデックスです。UNIQUEインデックス、通常のインデックス(非ユニーク)、FULLTEXTインデックス、SPATIALインデックスなどがこれにあたります。
- どのように機能するか: InnoDBでは、リーフノードにインデックス付けされたカラムの値と、対応する行の主キー値を格納します。MyISAMでは、リーフノードにインデックス付けされたカラムの値と、対応する行の物理的なアドレス(行ポインタ)を格納します。
- 重要な点: 前述の通り、InnoDBではセカンダリインデックスを使った検索は、最終的にクラスタ化インデックス(主キー)を経由してデータ行を取得します。MyISAMでは物理ポインタを使うため、この二段階アクセスはありません。ただし、MyISAMはテーブルロックのため同時実行性が低いという欠点があります。
5. CREATE INDEX
文の基本構文
MySQLでインデックスを作成するには、主にCREATE INDEX
文またはALTER TABLE
文を使用します。ここではまずCREATE INDEX
文の基本的な構文を見てみましょう。
sql
CREATE [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name
ON table_name (column1 [(length)] [ASC|DESC], column2 [(length)] [ASC|DESC], ...)
[index_option] ...;
各要素の意味は以下の通りです。
CREATE
: インデックスを作成することを指定するキーワードです。[UNIQUE|FULLTEXT|SPATIAL]
: オプションでインデックスのタイプを指定します。UNIQUE
: ユニークインデックスを作成します。インデックス付けされるカラムの組み合わせに重複する値を許可しません。FULLTEXT
: 全文検索インデックスを作成します。SPATIAL
: 空間インデックスを作成します。- 何も指定しない場合は、通常の(非ユニークな)B-Treeインデックスが作成されます。
INDEX index_name
: 作成するインデックスの名前を指定します。インデックス名はテーブル内でユニークである必要があります。通常、idx_テーブル名_カラム名
のような命名規則が使われます。インデックス名を省略すると、MySQLが自動的に命名します(通常、最初のカラム名に基づいた名前になります)が、明示的に指定する方が管理しやすいため推奨されます。ON table_name
: インデックスを作成するテーブルの名前を指定します。(column1 [(length)] [ASC|DESC], column2 [(length)] [ASC|DESC], ...)
: インデックスを作成するカラムをリストで指定します。- 単一のカラム名を指定すると、そのカラムに対するインデックスが作成されます。
- 複数のカラム名をカンマ区切りで指定すると、それらのカラムを組み合わせた複合カラムインデックス(Compound Index)が作成されます。カラムの順序は重要です。
(length)
:VARCHAR
,CHAR
,TEXT
,BLOB
型のカラムに対して、カラム値の先頭から指定したバイト数(または文字数)にのみインデックスを作成する「接頭辞インデックス (Prefix Index)」を作成する場合に指定します。これによりインデックスサイズを小さく抑えられますが、完全な値による検索や範囲検索には制限が出ます。[ASC|DESC]
: MySQL 8.0以降では、インデックス内の各カラムのソート順(昇順または降順)を指定できます。これにより、ORDER BY col ASC
やORDER BY col DESC
といったクエリで、ソート処理をスキップしてインデックス順にデータを読み込むことが可能になり、パフォーマンスが向上する可能性があります。指定しない場合はデフォルトで昇順 (ASC) になります。
[index_option] ...
: オプションでインデックスに関する設定を指定します。後述します。
ALTER TABLE
文を使ったインデックス作成は以下の構文になります。多くの場合、こちらの方がより一般的です。
sql
ALTER TABLE table_name
ADD [UNIQUE|FULLTEXT|SPATIAL] INDEX [index_name]
(column1 [(length)] [ASC|DESC], ...);
ALTER TABLE
文は、既存のテーブルにインデックスを追加・削除・変更する際に使用します。機能的にはCREATE INDEX
文とほぼ同じですが、ALTER TABLE
はテーブル構造の変更の一部としてインデックスを扱います。
6. 単一カラムインデックスの作成
最もシンプルなインデックスは、一つのカラムに対して作成する単一カラムインデックスです。
例えば、users
テーブルがあり、ユーザーのメールアドレスで検索することが多いとします。
sql
-- users テーブル構造例
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
registration_date DATETIME,
-- ... その他のカラム
);
email
カラムに対する検索を高速化するために、このカラムにインデックスを作成します。
sql
-- email カラムにインデックスを作成
CREATE INDEX idx_users_email ON users (email);
または、ALTER TABLE
を使用する場合:
sql
-- ALTER TABLE で email カラムにインデックスを作成
ALTER TABLE users ADD INDEX idx_users_email (email);
これで、WHERE email = '...'
といったクエリがインデックスを利用して高速に実行されるようになります。
sql
-- インデックス利用の例
SELECT id, username FROM users WHERE email = '[email protected]';
このクエリは、idx_users_email
インデックスを使って、指定されたメールアドレスを持つ行を素早く見つけ出します。
もしメールアドレスが一意である必要があるなら、UNIQUE INDEX
を作成することもできます。
sql
-- email カラムにユニークインデックスを作成
CREATE UNIQUE INDEX idx_users_email_unique ON users (email);
または
sql
-- ALTER TABLE で email カラムにユニークインデックスを作成
ALTER TABLE users ADD UNIQUE INDEX idx_users_email_unique (email);
これにより、email
カラムに同じ値が重複して登録されるのを防ぐと同時に、email
による検索も高速化されます。ユニークインデックスは、等価検索においてマッチする行が最大1つであることが保証されるため、特に効率的です。
7. 複合カラムインデックス (Compound Index)
複合カラムインデックスは、複数のカラムを組み合わせたインデックスです。例えば、col1
, col2
, col3
の3つのカラムからなる複合インデックスを作成する場合の構文は以下のようになります。
sql
CREATE INDEX idx_table_col1_col2_col3 ON your_table (col1, col2, col3);
または
sql
ALTER TABLE your_table ADD INDEX idx_table_col1_col2_col3 (col1, col2, col3);
複合カラムインデックスは、複数のカラムを組み合わせた条件で検索やソートを行うクエリのパフォーマンスを向上させるために重要です。しかし、その効果はカラムを定義した順序に大きく依存します。これは「先頭一致の原則 (Leftmost Prefix Matching)」と呼ばれる特性によるものです。
7.1. 先頭一致の原則 (Leftmost Prefix Matching)
複合カラムインデックス (col1, col2, col3)
は、以下のインデックスとしても機能します。
(col1)
(col1, col2)
(col1, col2, col3)
しかし、以下の組み合わせに対しては、原則としてインデックス全体またはその一部として直接利用することはできません。
(col2)
または(col3)
単独(col2, col3)
(col1, col3)
(間にcol2
が抜けているため)
つまり、複合インデックスが利用されるためには、クエリの条件がインデックスの定義順序の先頭から連続したカラムを含んでいる必要があります。
例: users
テーブルに (last_name, first_name, registration_date)
という複合インデックス idx_users_name_date
があるとします。
sql
ALTER TABLE users ADD INDEX idx_users_name_date (last_name, first_name, registration_date);
このインデックスは以下のクエリに利用可能です:
WHERE last_name = '山田'
(先頭のlast_name
のみ利用)WHERE last_name = '山田' AND first_name = '太郎'
(last_name
とfirst_name
を利用)WHERE last_name = '山田' AND first_name = '太郎' AND registration_date > '2023-01-01'
(last_name
,first_name
,registration_date
全て利用)ORDER BY last_name, first_name
(ソートにも利用可能)
以下のクエリには、このインデックスは利用されにくい、または限定的な利用になる可能性があります:
WHERE first_name = '太郎'
(first_name
はインデックスの先頭ではないため)WHERE registration_date > '2023-01-01'
(registration_date
はインデックスの先頭ではないため)WHERE last_name = '山田' AND registration_date > '2023-01-01'
(first_name
が抜けているため、last_name
までしかインデックスを利用できないか、全く利用されない可能性もある)WHERE first_name = '太郎' AND last_name = '山田'
(カラムの順序がインデックスの定義順序と異なっていても、オプティマイザが並べ替えて利用できる場合が多いですが、先頭一致の原則に基づけばlast_name
の部分しか直接的な効率化には繋がりにくいと理解しておくと良いでしょう)
7.2. 複合カラムインデックスのカラム順序の選び方
複合カラムインデックスを設計する際、カラムの順序は非常に重要です。以下の点を考慮して順序を決定します。
WHERE
句での使用頻度と等価条件: クエリのWHERE
句で最も頻繁に、かつ等価条件(=
)で使われるカラムを先頭に配置するのが一般的です。等価条件はB-Treeインデックスで最も効率的に機能し、後続のカラムの検索範囲を絞り込むことができるためです。- カーディナリティ (Cardinality): カラムに含まれる一意な値の種類の多さをカーディナリティと呼びます。カーディナリティの高いカラム(例: メールアドレス、ユーザーID)を先頭に置くと、最初のカラムで検索範囲を大きく絞り込めるため、後続のカラムの検索効率も向上します。ただし、必ずしもカーディナリティが最も高いカラムを先頭にすれば良いというわけではなく、クエリパターンとの兼ね合いが重要です。
- クエリパターン全体への適合: 複数のクエリで共通して利用できるようなインデックスを設計するのが理想です。よく実行される複数のクエリの
WHERE
,JOIN
,ORDER BY
句に含まれるカラムを分析し、最も効率的な順序を検討します。 ORDER BY
句:ORDER BY
句が頻繁に使われる場合、ソート順序と一致するカラム順序でインデックスを作成することで、別途ソート処理 (Using filesort
) を回避できる可能性があります。例えば、ORDER BY col1 ASC, col2 DESC
のようなクエリが多い場合、col1
を昇順、col2
を降順にした複合インデックスを作成すると効果的です(MySQL 8.0+ の機能)。
具体例:
orders
テーブルに (customer_id, order_date, total_amount)
というカラムがあり、以下のクエリが頻繁に実行されるとします。
- 「特定の顧客の過去の注文を日付順に見る」:
SELECT ... FROM orders WHERE customer_id = ? ORDER BY order_date DESC
- 「特定の日付以降の、特定の顧客の注文を見る」:
SELECT ... FROM orders WHERE customer_id = ? AND order_date > ?
この場合、customer_id
で絞り込んだ後、order_date
でフィルタリングまたはソートすることが多いと考えられます。したがって、(customer_id, order_date)
という複合インデックスが有効でしょう。customer_id
が等価条件で使われることが多く、かつ絞り込み効果が期待できるため、これを先頭に置きます。order_date
は範囲条件やソートに使われるため、次に来ます。
sql
ALTER TABLE orders ADD INDEX idx_orders_customer_date (customer_id, order_date);
このインデックスは、上記のクエリパターンに効率的に機能します。customer_id
で素早く絞り込み、その上でorder_date
に対する条件処理やソートをインデックスを使って効率的に行えます。
8. ユニークインデックス (UNIQUE INDEX
)
ユニークインデックスは、インデックス付けされたカラム(または複合インデックスの場合はカラムの組み合わせ)に重複する値が格納されるのを禁止するインデックスです。主キー(PRIMARY KEY
)は自動的にユニークインデックスになります。
sql
CREATE UNIQUE INDEX idx_table_unique_col ON your_table (unique_column);
または
sql
ALTER TABLE your_table ADD UNIQUE INDEX idx_table_unique_col ON your_table (unique_column);
ユニークインデックスの主な目的は以下の2つです。
- データの整合性維持: 重複データの挿入や更新を防ぎ、指定したカラムの値が一意であることを保証します。
- 検索パフォーマンス向上: ユニークインデックスを使った等価検索は、マッチする行が最大1つであることが確定しているため、通常のインデックスよりも早く検索を終了できる場合があります。
例えば、ユーザーのメールアドレスは重複してはいけない場合にユニークインデックスを使います。
sql
ALTER TABLE users ADD UNIQUE INDEX uix_users_email (email);
このインデックスを作成すると、既に存在するメールアドレスと同じ値をINSERT
またはUPDATE
しようとすると、エラーが発生して処理が失敗します。
9. 全文検索インデックス (FULLTEXT INDEX
)
全文検索インデックスは、大量のテキストデータの中から特定のキーワードを含むドキュメント(行)を検索するために使用します。VARCHAR
, CHAR
, TEXT
型などのカラムに対して作成できます。
sql
CREATE FULLTEXT INDEX fts_articles_content ON articles (content);
または
sql
ALTER TABLE articles ADD FULLTEXT INDEX fts_articles_content ON articles (content);
全文検索インデックスを作成すると、通常のLIKE '%keyword%'
のような部分一致検索よりもはるかに高速で柔軟なテキスト検索が可能になります。検索にはMATCH() AGAINST()
構文を使用します。
“`sql
— 自然言語モードでの検索
SELECT title, content
FROM articles
WHERE MATCH(content) AGAINST(‘MySQL インデックス’ IN NATURAL LANGUAGE MODE);
— BOOLEANモードでの検索 (より詳細な制御が可能)
SELECT title, content
FROM articles
WHERE MATCH(content) AGAINST(‘+MySQL +インデックス -遅い’ IN BOOLEAN MODE);
— ‘+’ は必須キーワード、’-‘ は除外キーワード
“`
FULLTEXTインデックスは、ストップワード(the
, a
, is
など、検索対象から除外される一般的な単語)のリストを使用したり、最小/最大単語長を設定したりすることができます。これらの設定は、MySQLのサーバー変数で制御されます。
FULLTEXTインデックスは通常のB-Treeインデックスとは異なる仕組みで動作するため、使用できるストレージエンジン(InnoDBとMyISAM)や、細かい挙動(最小単語長、ストップワードなど)に注意が必要です。
10. 空間インデックス (SPATIAL INDEX
)
空間インデックスは、GIS(地理情報システム)で使われる空間データ型(GEOMETRY
, POINT
, LINESTRING
, POLYGON
など)に対する検索を高速化します。インデックスタイプとしてSPATIAL
を指定します。
sql
CREATE TABLE locations (
id INT PRIMARY KEY,
name VARCHAR(100),
coordinates POINT NOT NULL,
SPATIAL INDEX spi_locations_coords (coordinates)
);
または既存テーブルに追加する場合:
sql
ALTER TABLE locations ADD SPATIAL INDEX spi_locations_coords (coordinates);
空間インデックスは、MBRContains()
, MBRWithin()
, MBROverlaps()
などの空間関数を使ったクエリで利用されます。これらの関数は、最小外接矩形(MBR)を比較することで、空間オブジェクト間の包含関係や交差関係を効率的に判断します。
sql
-- 特定の四角形領域に含まれる場所を検索
SELECT name FROM locations
WHERE MBRContains(
ST_GEOMFROMTEXT('POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))'),
coordinates
);
空間インデックスは、位置情報を含むアプリケーションなどで、効率的な位置ベースの検索を実現するために不可欠です。
11. インデックスオプション (index_option
)
CREATE INDEX
またはALTER TABLE ... ADD INDEX
文では、インデックスの挙動や特性を制御するための様々なオプションを指定できます。
sql
CREATE INDEX index_name ON table_name (column_list)
USING index_type
KEY_BLOCK_SIZE = value
WITH PARSER parser_name
COMMENT 'string'
VISIBLE / INVISIBLE;
主なオプション:
USING index_type
: 使用するインデックスのタイプを明示的に指定します。BTREE
,HASH
,RTREE
があります。省略した場合、ストレージエンジンのデフォルト(InnoDBはBTREE)が使用されます。通常、明示的に指定する必要はありませんが、MEMORYテーブルでHASH
インデックスを作成したい場合などに使用します。KEY_BLOCK_SIZE = value
: ディスク上のインデックスブロックのサイズをバイト単位で指定します。MyISAMストレージエンジンで使用されますが、InnoDBではほとんど効果がありません。高度なチューニングオプションであり、通常はデフォルト値で問題ありません。WITH PARSER parser_name
:FULLTEXT
インデックスを作成する際に、使用するテキストパーサーを指定します。例えば、日本語の分かち書きを行うMeCabなどのプラグインパーサーを指定する場合に使用します。COMMENT 'string'
: インデックスにコメントを追加します。インデックスの目的や作成理由などをメモしておくと管理に役立ちます。SHOW INDEX
で確認できます。VISIBLE / INVISIBLE
: (MySQL 8.0以降) インデックスをクエリオプティマイザに見せるか(VISIBLE、デフォルト)、隠すか(INVISIBLE)を指定します。INVISIBLEなインデックスは、デフォルトではクエリの実行計画の候補になりません。これは、インデックスを削除する前に、それが使われなくなってもパフォーマンスに影響がないかテストするのに便利です。システム変数optimizer_switch='use_invisible_indexes=on'
を設定することで、INVISIBLEなインデックスもオプティマイザに使用させることができます。
12. インデックスの削除 (DROP INDEX
)
不要になったインデックスは削除することで、書き込み性能の向上やディスク容量の節約につながります。インデックスの削除は以下の構文で行います。
sql
DROP INDEX index_name ON table_name;
または
sql
ALTER TABLE table_name DROP INDEX index_name;
どちらの構文でも同じ結果が得られます。DROP INDEX
は、テーブルの構造変更の一部として実行されます。
注意点: 主キーインデックスは、ALTER TABLE table_name DROP PRIMARY KEY;
のように専用の構文で削除します(ただし、ほとんどのテーブルは主キーが必須または強く推奨されます)。ユニーク制約や主キー制約によって自動作成されたインデックスを削除する場合、その制約も一緒に削除されることに注意してください。
インデックスの削除は、特に大きなテーブルの場合、時間がかかる可能性があり、テーブルにロックがかかる場合があります。実行する際はシステムへの影響を考慮する必要があります。
13. インデックスの確認 (SHOW INDEX
)
テーブルにどのようなインデックスが作成されているかを確認するには、SHOW INDEX
文を使用します。
sql
SHOW INDEX FROM table_name;
または
sql
SHOW KEYS FROM table_name;
このコマンドを実行すると、指定したテーブルに存在する全てのインデックスに関する情報がリスト形式で表示されます。出力される主なカラムは以下の通りです。
Table
: インデックスが作成されているテーブルの名前。Non_unique
: 0ならばユニークインデックス(または主キー)、1ならば通常の(非ユニークな)インデックス。Key_name
: インデックスの名前。主キーは通常PRIMARY
という名前になります。Seq_in_index
: 複合カラムインデックスの場合、インデックス内のカラムの順序を示します(1から始まる)。Column_name
: インデックスの一部となっているカラムの名前。Collation
: カラムがインデックス内でどのようにソートされているかを示します(A
は昇順、D
は降順)。MySQL 8.0以降でDESC
インデックスがサポートされています。Cardinality
: インデックスに含まれる一意な値のおおよその数。テーブルの行数に対するカーディナリティが高いほど、そのカラムでインデックス検索を行った際の絞り込み効果が期待できます。この値は統計情報に基づいており、常に正確とは限りません。ANALYZE TABLE
で更新できます。Sub_part
: 接頭辞インデックスの場合、インデックスに含められている接頭辞の長さをバイト数で示します。カラム全体にインデックスが貼られている場合はNULL
です。Packed
: インデックスがパックされているかどうか。通常はNULL
。Nullable
: インデックスに含まれるカラムがNULLを許可するかどうか。YES
の場合、NULL
値を含む行もインデックスに格納されます。Index_type
: インデックスのタイプ (BTREE
,HASH
,FULLTEXT
,SPATIAL
など)。Comment
: インデックス作成時に指定したコメント。Index_comment
: 上記Comment
と同じ情報。Visible
: (MySQL 8.0以降) インデックスがVISIBLE
かINVISIBLE
か。
Cardinality
カラムは、インデックスがどの程度ユニークな値を保持しているかを示す重要な指標です。カーディナリティが高い(値の種類が多い)インデックスは、特定の値を検索する際に効率的に行を絞り込むことができる可能性が高いです。ただし、カーディナリティが低いカラム(例:性別、フラグカラムなど、数種類の値しかない)でも、そのカラムがクエリのWHERE
句で頻繁に使われ、かつ絞り込み対象の行数が少ない場合はインデックスが有効なことがあります。
14. インデックスの「効果」:メリットとデメリット
インデックスはデータベースパフォーマンス改善の強力なツールですが、万能ではありません。メリットとデメリットを理解し、適切に使用することが重要です。
14.1. インデックスのメリット
- 検索クエリ(
SELECT
)の高速化: これがインデックスの最大のメリットです。特にWHERE
、JOIN
、ORDER BY
、GROUP BY
句で指定されたカラムに対するクエリが劇的に速くなります。フルテーブルスキャンを回避し、必要なデータに直接アクセスできるため、データ量が多くなるほど効果が顕著になります。 - ディスクI/Oの削減: フルテーブルスキャンではテーブル全体をディスクから読み込む必要がありますが、インデックスを使用すると、インデックスデータと必要な行データだけを読み込めば済むため、ディスクI/Oの量が減り高速化に繋がります。
- ユニーク制約によるデータ整合性の確保:
UNIQUE INDEX
やPRIMARY KEY
は、データの重複を防ぎ、データの一意性を保証します。 - カバリングインデックスによるクエリの高速化: (応用) クエリに必要な全てのカラムがインデックス自体に含まれている場合、データベースはデータ行本体を読み込む必要がなく、インデックスだけを参照してクエリを完結させることができます(
EXPLAIN
のExtraカラムにUsing index
と表示される)。これを「カバリングインデックス (Covering Index)」と呼び、非常に高いパフォーマンスが得られます。
14.2. インデックスのデメリット
- 書き込み操作(
INSERT
,UPDATE
,DELETE
)の遅延: データ行を追加、更新、削除する際には、テーブルのデータ本体だけでなく、関連するインデックスも更新する必要があります。インデックスが多いほど、これらの書き込み操作にかかる時間が増加し、パフォーマンスが低下します。特にUPDATE
文でインデックスカラムの値を変更する場合や、DELETE
文でインデックスに多くのエントリを持つ行を削除する場合に負荷が高くなります。 - ディスク容量の消費: インデックスデータはディスク上に保存されるため、インデックスが多いほど必要なディスク容量が増加します。特に、カーディナリティが低いカラムに無数のインデックスを作成したり、長い文字列カラムに接頭辞なしでインデックスを貼ったりすると、インデックスサイズが大きくなりがちです。
- クエリオプティマイザへの影響と管理コスト: インデックスが増えると、MySQLのクエリオプティマイザはどのインデックスを使用するのが最適かを判断するための選択肢が増えます。最適なインデックスを選択できない場合、かえってパフォーマンスが低下することもあります。また、インデックスはテーブルの変更に伴ってメンテナンス(統計情報の更新や断片化解消など)が必要になる場合があり、管理コストがかかります。
- 小さなテーブルでの効果: テーブルのデータ量が非常に少ない場合、インデックスを使うオーバーヘッド(インデックスを辿る処理)が、フルテーブルスキャンにかかる時間よりも大きくなることがあります。小さなテーブルでは、インデックスのメリットは限定的です。
これらのデメリットを考慮し、インデックスは闇雲に作成するのではなく、実際のクエリパターンやデータアクセス特性に基づいて慎重に設計・適用する必要があります。書き込みが多いテーブルでは、必要なインデックスを最小限に抑えることが重要になる場合もあります。
15. インデックス設計のベストプラクティス
効果的なインデックス設計は、データベースパフォーマンスチューニングの鍵です。以下のベストプラクティスを参考にしてください。
- すべてのカラムにインデックスを貼らない: デメリットで述べたように、インデックスはコストを伴います。必要最低限のインデックスを設計します。
- クエリの
WHERE
,JOIN
,ORDER BY
,GROUP BY
句を分析する: これらの句で頻繁に使用されるカラムは、インデックスの候補となります。最も実行頻度が高く、パフォーマンスが重要なクエリから改善を目指しましょう。 - カーディナリティを考慮する: カーディナリティが高いカラムは、等価検索や範囲検索で効率的な絞り込みが期待できます。しかし、カーディナリティが低いカラムでも、その値を持つ行数が少ない場合はインデックスが有効です。重要なのは「インデックスを使ってアクセスする行数を減らせるか」です。
- 複合カラムインデックスのカラム順序を慎重に検討する: 先頭一致の原則を理解し、クエリパターンに最適な順序(通常は等価条件で使われ、絞り込み効果の高いカラムを先頭に)でカラムを並べます。
EXPLAIN
で検証が不可欠です。 - 必要に応じて接頭辞インデックスを使用する:
VARCHAR
,TEXT
など、長い文字列カラム全体にインデックスを貼るとサイズが大きくなりすぎたり、一部のインデックスタイプではサポートされていなかったりします。その場合、先頭の一部にインデックスを貼る接頭辞インデックスを検討します。ただし、接頭辞長が短すぎるとカーディナリティが低下し効果が薄れる可能性があるため、適切な長さを選択する必要があります。SELECT COUNT(DISTINCT LEFT(column_name, length)) / COUNT(*) FROM table_name;
のようにして、接頭辞長ごとのカーディナリティ比率を確認すると参考になります。 - 小さなテーブルにはインデックスは不要なことが多い: 行数が数千以下のような小さなテーブルでは、フルテーブルスキャンの方が高速な場合があります。インデックスのオーバーヘッドが大きくなるため、無理にインデックスを作成する必要はありません。
- インデックスの使用状況を監視する: 作成したインデックスが実際にクエリで使用されているか確認することが重要です。MySQL 5.1以降では、
performance_schema.table_io_waits_summary_by_index_usage
テーブルなどでインデックスの使用状況を確認できます(設定が必要です)。MySQL 5.6以降では、performance_schema. table_io_waits_summary_by_index_usage
テーブルが利用できます。MySQL 8.0では、sys.schema_unused_indexes
ビューで未使用のインデックスを簡単に特定できます。 - 不要なインデックスは削除する: 使用されていないインデックスは、書き込み性能を低下させ、ディスク容量を無駄に消費するだけです。定期的に使用状況を確認し、不要なインデックスは削除しましょう。
- 書き込み性能とのトレードオフを考慮する: 読み込み(SELECT)性能を追求するために無数のインデックスを作成すると、書き込み(INSERT, UPDATE, DELETE)性能が著しく低下する可能性があります。システムの特性(参照が多いのか、更新が多いのか)を考慮し、両方のバランスを取る必要があります。
EXPLAIN
を使ってクエリの実行計画を確認する: これが最も重要です。作成したインデックスが意図通りに使われているか、あるいはどのインデックスを作成すればクエリが高速化するかを判断するには、EXPLAIN
コマンドでクエリの実行計画を分析することが不可欠です(次項で詳しく解説)。
16. EXPLAIN
を使ったインデックス効果の確認
インデックス設計が適切かどうか、または特定のクエリがインデックスをどのように利用しているかを確認するための最も重要なツールがEXPLAIN
コマンドです。EXPLAIN
は、MySQLがどのようにクエリを実行するか(実行計画)を示してくれます。
使い方は簡単です。確認したいSELECT
クエリの先頭にEXPLAIN
を付けるだけです。
sql
EXPLAIN SELECT id, username FROM users WHERE email = '[email protected]';
このコマンドを実行すると、クエリの結果セットではなく、クエリの実行計画を示す1行以上の情報が表示されます。表示される主要なカラムと、インデックスとの関連について説明します。
カラム | 説明 | インデックスとの関連 |
---|---|---|
id |
クエリ内の各SELECT 文を識別する番号。複雑なクエリ(UNION、サブクエリなど)では複数の行になります。 |
|
select_type |
SELECT 文のタイプ。SIMPLE (単純なSELECT), PRIMARY (最も外側のSELECT), SUBQUERY , UNION , DERIVED (FROM句のサブクエリ) など。 |
|
table |
操作対象のテーブル。 | |
partitions |
(MySQL 5.1以降) クエリがアクセスするパーティション。 | パーティションインデックスが利用されているかなど。 |
type |
テーブルへのアクセスタイプ。インデックス効果を判断する上で最も重要なカラムの一つです。 | 実行計画の効率を示します。左に行くほど効率的です。ALL : フルテーブルスキャン。最も遅いタイプ。インデックスが利用されていないか、効果がない場合。index : フルインデックススキャン。インデックス全体を順番に読み込む。ALL より速いが、key を使ったアクセスより遅い。range : インデックスを使って特定の範囲の行を取得。範囲検索(BETWEEN , > , < など)で利用される。比較的効率的。ref : 非ユニークインデックスまたは複合インデックスの接頭辞を使って、単一の値にマッチする行を検索。結合の右側のテーブルアクセスなどにも使われる。効率的。eq_ref : JOIN において、結合条件の両方のテーブルで主キーまたはユニークインデックスが使われ、左側のテーブルの1行に対して右側のテーブルで正確に1行がマッチする場合。非常に効率的。const , system : クエリが定数に絞り込まれる場合。最も効率的。 |
possible_keys |
MySQLがこのクエリに使用できると判断したインデックスの候補リスト。 | MySQLがインデックスの存在を認識しているか確認できます。ここに表示されていても、key で使用されるとは限りません。 |
key |
MySQLが実際にこのクエリの実行に使用することを選択したインデックスの名前。NULL の場合はインデックスが使われていません。 |
どのインデックスが使われているかを示します。possible_keys に含まれていても、ここに表示されなければ使われていません。 |
key_len |
使用されたインデックスの長さ(バイト単位)。複合インデックスの場合、どのカラムまでインデックスが使用されたかを示唆します。 | 複合インデックスの先頭一致の原則がどの範囲まで適用されたかなどを判断するのに役立ちます。 |
ref |
key で使用されたインデックスの各値を比較するために使用されるカラムまたは定数。 |
定数(const )、システムカラム(func )、別のテーブルのカラム(db.table.col_name )などが表示されます。 |
rows |
クエリ実行のためにMySQLが読み込むと予測される行数。この数値が小さいほど効率的です。 | クエリオプティマイザの推定値であり、正確ではない場合もありますが、インデックスの効果を測る上で重要な指標です。インデックスがうまく機能している場合、この数値は小さくなります。 |
filtered |
(MySQL 5.1以降) rows で読み込んだ行のうち、WHERE 句などの条件によってフィルタリングされ、最終的に結果セットに残る行の割合(パーセント)。 |
この値が高い(例えば100%に近い)ほど、読み込んだ行の大部分が結果として利用されることを意味します。Using where と組み合わせて、インデックスで絞り込めなかった追加フィルタリングのコストを示唆します。 |
Extra |
MySQLがクエリ実行に関して追加で情報を表示するカラム。インデックスやクエリの非効率性を示す重要な情報が含まれます。 | – Using index : クエリに必要な全ての情報がインデックスに含まれている(カバリングインデックス)。データ行本体へのアクセス不要。非常に効率的。– Using where : インデックスだけでは条件を満たせず、データ行を読み込んだ後に追加のフィルタリングが必要。– Using temporary : 結果を保持するための一時テーブルが作成される。GROUP BY , ORDER BY , DISTINCT などでインデックスが利用できない場合に発生しやすく、パフォーマンスが低下することがある。– Using filesort : MySQLがデータをソートするためにインデックスを使えず、外部ソート処理(メモリ上またはディスク上)を行っている。ORDER BY 句でインデックスが使えない場合に発生しやすく、パフォーマンスが低下することがある。– Using join buffer : JOIN の種類によっては結合バッファが使われていることを示す。 |
EXPLAIN
の出力を分析する際のチェックポイント:
type
がALL
になっていないか? (range
,ref
,eq_ref
,const
などが理想)key
が期待通りのインデックスになっているか? (NULL
の場合はインデックスが使われていない)rows
の値が妥当か? テーブルサイズに対して大きすぎないか?Extra
にUsing index
が表示されているか? (カバリングインデックスが利用できているか)Extra
にUsing temporary
やUsing filesort
が表示されていないか? (これらはインデックスで避けたい非効率な処理)
これらの情報を見ることで、どのインデックスが利用されているか、そしてそのインデックスがクエリに対してどの程度効率的に機能しているかを把握できます。EXPLAIN
の結果を見て、インデックスが使われていない、または非効率なアクセスになっている場合は、インデックスの作成、修正、あるいはクエリの書き換えを検討する必要があります。
17. 応用的なインデックスの話題
17.1. 接頭辞インデックス (Prefix Index)
前述の通り、VARCHAR
, TEXT
, BLOB
のような長いカラムにインデックスを貼る場合、インデックスサイズを小さく抑えるためにカラム値の先頭Nバイト(または文字)のみにインデックスを貼るのが接頭辞インデックスです。
sql
-- VARCHAR(255) カラムの先頭100バイトにインデックスを貼る
ALTER TABLE articles ADD INDEX idx_articles_title_prefix ON articles (title(100));
利点:
* インデックスのサイズが小さくなり、ディスク容量の節約やインデックス操作の高速化に繋がります。
欠点:
* 接頭辞部分だけでは値が一意にならない場合、完全な値の比較のためにデータ行本体を読み込む必要があります。
* 接頭辞に含まれない部分に対する検索条件 (WHERE col LIKE '%suffix'
) や範囲検索には利用できません。
* 接頭辞長を短くしすぎるとカーディナリティが低下し、インデックスの効果が薄れます。
接頭辞長を決定する際は、カーディナリティが大きく低下しないような適切な長さを選ぶ必要があります。通常、カラムの全長に対する接頭辞部分のカーディナリティが十分高くなるような長さを試行錯誤して決定します。
17.2. 関数ベースインデックス (Functional Index / Generated Column Index)
MySQL 8.0以降では、カラムに対する関数や式の計算結果にインデックスを作成することができます。これにより、クエリのWHERE
句で関数を使った条件を指定した場合でもインデックスを利用できるようになります。
構文は以下のようになります。
sql
CREATE INDEX index_name ON table_name ((expression));
expression
の部分に関数や式を指定します。例えば、JSONカラム内の特定のキーの値にインデックスを貼りたい場合や、大文字/小文字を区別しない検索のためにLOWER()
関数の結果にインデックスを貼りたい場合などに使用します。
“`sql
— email カラムを全て小文字にした結果にインデックスを作成(大文字/小文字を区別しない検索用)
ALTER TABLE users ADD INDEX idx_users_email_lower ((LOWER(email)));
— このインデックスは以下のクエリで利用される可能性がある
SELECT id, username FROM users WHERE LOWER(email) = ‘[email protected]’;
“`
もう一つの方法は、生成カラム (Generated Column) を作成し、その生成カラムにインデックスを貼る方法です。生成カラムは、他のカラムの値から計算されて格納(または仮想的に生成)されるカラムです。
“`sql
— email_lower という生成カラムを作成し、そのカラムにインデックスを貼る
ALTER TABLE users ADD email_lower VARCHAR(100) GENERATED ALWAYS AS (LOWER(email)),
ADD INDEX idx_users_email_lower (email_lower);
— このインデックスは以下のクエリで利用される可能性がある
SELECT id, username FROM users WHERE email_lower = ‘[email protected]’;
— または WHERE LOWER(email) = ‘[email protected]’ (オプティマイザが生成カラムに書き換える場合)
“`
関数ベースインデックスや生成カラムインデックスは、特定の複雑なクエリパターンに対して効果的なインデックス戦略を提供します。
17.3. 不可視インデックス (Invisible Index)
MySQL 8.0以降で導入された機能です。インデックスを物理的に削除することなく、クエリオプティマイザがそのインデックスを使用しないように設定できます。
インデックス作成時にINVISIBLE
オプションを付けます。
sql
CREATE INDEX idx_users_registration_date ON users (registration_date) INVISIBLE;
既存のインデックスをINVISIBLEにするにはALTER TABLE
を使います。
sql
ALTER INDEX idx_users_registration_date INVISIBLE;
-- または ALTER TABLE users ALTER INDEX idx_users_registration_date INVISIBLE;
VISIBLEに戻す場合はVISIBLE
オプションを使います。
sql
ALTER INDEX idx_users_registration_date VISIBLE;
-- または ALTER TABLE users ALTER INDEX idx_users_registration_date VISIBLE;
用途:
* インデックスを削除する前に、そのインデックスが実際に使用されなくなってもパフォーマンスに影響がないか検証したい場合。INVISIBLE
にすることで、そのインデックスがない場合とほぼ同じ実行計画をテストできます。影響がなければ安心して削除できます。
* 一時的に特定のインデックスの使用を避けたい場合。
デフォルトではINVISIBLEなインデックスはオプティマイザに使用されませんが、システム変数 optimizer_switch='use_invisible_indexes=on'
を設定することで、一時的に使用させることができます。
18. インデックスのメンテナンス
インデックスは、テーブルのデータが変更されるたびに更新されますが、時間とともにインデックスの統計情報が古くなったり、物理的な構造が断片化したりすることがあります。これにより、クエリオプティマイザが最適な実行計画を選択できなくなったり、インデックスを辿る効率が低下したりする可能性があります。
定期的なインデックスのメンテナンスとして、以下の操作が有効です。
ANALYZE TABLE table_name;
: テーブルおよびそのインデックスの統計情報を収集し、更新します。クエリオプティマイザは最新の統計情報に基づいて最適な実行計画を決定するため、このコマンドは非常に重要です。データが頻繁に更新・削除されるテーブルでは、定期的に実行することを推奨します。OPTIMIZE TABLE table_name;
: InnoDBテーブルの場合、実質的にはテーブルの再作成とインデックスの再構築が行われます。これにより、データとインデックスの物理的な断片化が解消され、ディスク容量の解放やアクセス効率の向上に繋がる可能性があります。ただし、テーブルのサイズによっては非常に時間がかかり、その間テーブルにロックがかかる場合があるため、本番環境での実行には注意が必要です。MySQL 5.6以降のInnoDBではオンラインDDLが強化されており、多くの場合はテーブル全体をロックせずに実行できますが、それでも大きな負荷がかかる可能性はあります。
統計情報の更新(ANALYZE TABLE
)は比較的負荷が小さく頻繁に実行しても問題ありませんが、OPTIMIZE TABLE
はより大きな操作なので、メンテナンスウィンドウなどで慎重に実行を検討します。
19. まとめ:適切なインデックスはパフォーマンス改善の鍵
この記事では、MySQLのインデックスについて、その基本的な概念、種類、作成方法(CREATE INDEX
, ALTER TABLE ADD INDEX
)、削除方法(DROP INDEX
)、確認方法(SHOW INDEX
)を詳しく見てきました。また、インデックスがクエリパフォーマンスに与えるメリットとデメリット、効果的なインデックス設計のためのベストプラクティス、そしてEXPLAIN
を使った実行計画の分析方法についても詳細に解説しました。
インデックスは、データ検索を高速化し、データベースシステムの応答性を向上させるための最も基本的な手段です。しかし、インデックスを作成すればするほど良いというわけではありません。多すぎるインデックスは、書き込み性能の低下やディスク容量の消費を招きます。
最も重要なのは、あなたのアプリケーションがどのようなクエリを、どのくらいの頻度で実行しているかを理解することです。そして、そのクエリのWHERE
, JOIN
, ORDER BY
, GROUP BY
句に着目し、EXPLAIN
コマンドで実行計画を分析しながら、パフォーマンスボトルネックとなっている部分を特定します。その上で、最も効果が期待できるインデックスを慎重に設計・作成し、再びEXPLAIN
で改善されたか確認するというPDCAサイクルを回すことが、パフォーマンスチューニングにおいては不可欠です。
特に、複合カラムインデックスの「先頭一致の原則」や、InnoDBにおける主キーとセカンダリインデックスの関係は、インデックスの仕組みを理解し、効果的な設計を行う上で非常に重要です。
インデックスは銀の弾丸ではありませんが、適切に使いこなせば、データベースパフォーマンスを大きく向上させることができます。この記事が、あなたのMySQLデータベースのパフォーマンス改善に役立つことを願っています。
20. 参考文献/さらに学ぶために
- MySQL 8.0 Reference Manual: 9.1.15. Optimizer Notes (インデックスの使用条件など)
- MySQL 8.0 Reference Manual: 9.1.16. EXPLAIN Output Format
- MySQL 8.0 Reference Manual: 15.7.3. CREATE INDEX Statement
- MySQL 8.0 Reference Manual: 15.1.20. CREATE TABLE Statement (インデックス定義についても記載)
- MySQL 8.0 Reference Manual: 15.1.9. ALTER TABLE Statement (インデックスのADD/DROPなど)
- 高性能MySQL (High Performance MySQL) – インデックスに関する章が非常に詳細で参考になります。
これらの公式ドキュメントや専門書を参考に、さらに理解を深めていくことをお勧めします。パフォーマンスチューニングは奥が深く、継続的な学習と実践が重要です。