はい、承知いたしました。MySQLのインデックス確認方法をマスターするための詳細な解説を含む、約5000語の記事を作成します。
これで安心!MySQL インデックスの確認方法をマスター
1. はじめに:なぜインデックス確認が重要なのか
データベースのパフォーマンスを語る上で、インデックスは避けて通れない重要な要素です。適切なインデックスが存在するかどうか、そしてそれらが効率的に使用されているかどうかは、データベースのクエリ実行速度に劇的な影響を与えます。インデックスがない、あるいは不適切なインデックスしか存在しない場合、データベースはテーブル全体をスキャンする必要があり、データ量が増えるにつれてクエリは極めて遅くなります。
しかし、ただインデックスを作成すれば良いというわけではありません。必要以上に多くのインデックスを作成したり、効果の薄いインデックスを作成したりすると、データの書き込み(INSERT, UPDATE, DELETE)が遅くなったり、ディスク容量を浪費したりするデメリットが生じます。
したがって、データベースのパフォーマンスを最適化するためには、以下の点を常に把握している必要があります。
- 現在、どのようなインデックスがテーブルに存在するのか?
- それぞれのインデックスはどのようなカラムに、どのような順序で定義されているのか?
- 実行しているクエリに対して、データベース(MySQLのオプティマイザ)はどのインデックスを選択し、どのようにアクセスしているのか?
- インデックスが使われていないのはなぜか?
- 作成したインデックスは実際に効果を発揮しているのか、それとも使われずに放置されているのか?
これらの疑問に答えるための「インデックスの確認方法」をマスターすることは、データベース管理者(DBA)だけでなく、データベースを利用するすべての開発者にとって必須のスキルです。インデックスの現状を正確に把握し、クエリの実行計画を理解することで、初めて効果的なインデックス戦略を立て、パフォーマンスの問題を解決できるようになります。
この記事では、MySQLにおけるインデックスの様々な確認方法を、初心者の方にも理解できるよう丁寧に解説します。基本的なコマンドから、クエリの実行計画を分析する EXPLAIN
、さらに高度なインデックス利用状況の確認まで、具体的なコマンド例と出力の解釈を交えながら、インデックス確認のスキルを「マスター」することを目指します。この記事を読み終える頃には、きっとMySQLのインデックス確認に自信を持てるようになっているはずです。
2. MySQLインデックスの基礎知識
具体的な確認方法に入る前に、まずはMySQLのインデックスに関する基本的な知識をおさらいしておきましょう。
2.1. インデックスとは?
インデックスは、データベーステーブル内のデータを高速に検索するためのデータ構造です。例えるなら、本の巻末にある索引(インデックス)のようなものです。索引があれば、本の特定の内容がどのページにあるかを知るために、最初から最後までページをめくる必要はありません。索引を見て目的のキーワードが載っているページ番号を確認すれば、すぐにそのページを開くことができます。
データベースのインデックスも同様に、特定のカラムの値に基づいて、対応する行のデータがどこに格納されているかの物理的な位置情報を効率的に見つけ出すためのものです。これにより、テーブル全体を最初から最後まで読み込む「フルテーブルスキャン」を避けることができ、検索(SELECT)クエリのパフォーマンスを大幅に向上させます。
2.2. インデックスの種類
MySQLで主に使用されるインデックスのタイプにはいくつか種類があります。ストレージエンジン(InnoDB, MyISAMなど)によってサポートされるインデックスタイプや実装方法が異なりますが、ここではInnoDBエンジンでよく利用されるインデックスを中心に解説します。
- B-tree インデックス:
最も一般的で、InnoDBやMyISAMを含む多くのストレージエンジンでデフォルトとして使用されます。データがB-treeと呼ばれるツリー構造で整理されており、範囲検索(>, <, BETWEEN)、等価検索(=)、LIKE演算子(前方一致 ‘prefix%’)などに非常に有効です。主キー (PRIMARY KEY
) やユニークキー (UNIQUE KEY
) は通常B-treeインデックスとして実装されます。 - Hash インデックス:
キー値のハッシュ値を計算し、そのハッシュ値を基にデータ位置を探します。主にメモリベースのエンジン(如くMEMORYストレージエンジン)で使用されます。等価検索(=)には非常に高速ですが、範囲検索やソートには使えません。また、ハッシュ衝突の問題もあります。InnoDBでは明示的にHashインデックスを作成することはできませんが、アダプティブハッシュインデックスという機能で内部的に活用されることがあります。 - Full-text インデックス:
テキストデータ内のキーワード検索(全文検索)に特化したインデックスです。LIKE演算子よりも高度で柔軟な検索が可能です。主にMyISAMストレージエンジンで利用されてきましたが、MySQL 5.6以降のInnoDBでもサポートされています。 - Spatial (R-tree) インデックス:
地理空間データ(緯度・経度など)を効率的に検索するためのインデックスです。GIS(地理情報システム)関連のアプリケーションで使用されます。
本記事で主に確認対象となるのは、B-treeインデックスです。
2.3. インデックスのメリットとデメリット
- メリット:
- 検索(SELECT)の高速化: 特定の条件に合致する行を効率的に見つけ出せる。
- ソート(ORDER BY)の高速化: インデックス順にデータが格納されている場合、ソート処理が不要になるか、高速化される。
- JOIN処理の高速化: JOIN条件にインデックスが利用されることで、結合処理が効率化される。
- 集計(GROUP BY)の高速化: GROUP BY句にインデックスが利用されることで、集計処理が効率化される。
- ユニーク制約や主キー制約の実装: これらの制約は内部的にユニークインデックスによって保証されます。
- デメリット:
- 書き込み(INSERT, UPDATE, DELETE)処理の遅延: データの変更に伴い、インデックス構造も更新する必要があるため、処理コストが増加します。インデックスが多いほど、このオーバーヘッドは大きくなります。
- ディスク容量の消費: インデックス自身がデータ構造としてディスク上に保存されるため、テーブルのデータとは別に容量が必要になります。
- メンテナンスのコスト: インデックスが多すぎたり不適切だったりすると、管理が煩雑になります。
2.4. インデックスが活躍する場面
主に以下のSQL句で使用されるカラムに対してインデックスを作成することが効果的です。
WHERE
句: 検索条件として使用されるカラム。最もインデックスが頻繁に利用される場所です。JOIN
句: テーブル結合の条件として使用されるカラム。ORDER BY
句: ソートの基準となるカラム。GROUP BY
句: 集計の基準となるカラム。
ただし、インデックスが効果を発揮するのは、検索対象のデータがテーブル全体の行数に対して比較的少ない場合(数%程度)です。テーブルのほとんどの行にマッチするような条件の場合、インデックスを使ってもフルテーブルスキャンと比べて大きなメリットがないとオプティマイザが判断し、インデックスが使われないこともあります。
3. インデックスの基本確認方法
まずは、特定のテーブルに現在どのようなインデックスが存在するのかを確認する基本的なコマンドから見ていきましょう。
3.1. SHOW INDEX
コマンド
SHOW INDEX
コマンドは、指定したテーブルに定義されているすべてのインデックスに関する詳細情報を表示します。SHOW INDEXES
や SHOW KEYS
は SHOW INDEX
の同義語です。
3.1.1. 構文
sql
SHOW INDEX FROM table_name [FROM db_name] [WHERE where_condition];
-- または
SHOW INDEXES FROM table_name [FROM db_name] [WHERE where_condition];
-- または
SHOW KEYS FROM table_name [FROM db_name] [WHERE where_condition];
table_name
は確認したいテーブルの名前です。FROM db_name
を指定することで、現在使用しているデータベースとは異なるデータベースのテーブルのインデックスを確認できます。WHERE where_condition
を指定すると、特定の条件にマッチするインデックスだけを表示できます(例:WHERE Key_name = 'idx_user_name'
)。
3.1.2. 出力カラムの詳細な解説
SHOW INDEX
コマンドの出力は、インデックスごとに1行表示され、以下のカラムが含まれます。それぞれのカラムが何を示しているのかを理解することが重要です。
カラム名 | 説明 |
---|---|
Table |
インデックスが定義されているテーブルの名前。 |
Non_unique |
インデックスがユニークでない(0)か、ユニークである(1)かを示します。PRIMARY KEY または UNIQUE 制約を持つインデックスの場合、この値は0になります。通常のインデックスや複合インデックスの一部でユニーク性が保証されない場合は1になります。 |
Key_name |
インデックスの名前。PRIMARY KEY のインデックス名は常に PRIMARY になります。その他のインデックスは、作成時に指定した名前(CREATE INDEX index_name ON table_name(...) の index_name )になります。名前を指定しなかった場合は、MySQLが自動的に生成します。 |
Seq_in_index |
複合インデックスの場合、カラムがインデックス内で何番目(1から始まる)に使用されているかを示します。単一カラムのインデックスの場合は常に1です。 |
Column_name |
インデックスに含まれるカラムの名前。複合インデックスの場合、インデックスを構成する各カラムがこのカラムに表示されます。 |
Collation |
カラムがインデックス内でどのようにソートされるかを示します。A は昇順(Ascending)で、D は降順(Descending)です。ほとんどの場合、昇順で A が表示されます。 |
Cardinality |
インデックス内のユニークな値の推定数です。この値が高いほど、インデックスはそのカラムでの検索条件を絞り込むのに効果的である可能性が高いことを示します。Cardinality は統計情報に基づいて計算される推定値であり、正確な値ではありません。データが頻繁に更新されると、古い値になっている可能性があるため、ANALYZE TABLE で更新することもあります。 |
Sub_part |
インデックスにカラム値のプレフィックス(先頭部分)のみが使用されている場合、そのプレフィックスの長さをバイト単位で示します。カラム全体がインデックスに使用されている場合は NULL になります。テキストカラム(TEXT, BLOBなど)のインデックスでよく使用されます。 |
Packed |
キーがパックされている(圧縮されている)かどうかを示します。NULL または空文字列が表示されることがほとんどです。 |
Null |
インデックスに含まれるカラムが NULL 値を格納できる場合 (YES ) またはできない場合 (NO ) を示します。NULL が許可されるカラムにインデックスを作成すると、NULL 値もインデックスに含まれます。 |
Index_type |
インデックスのタイプを示します。一般的な値は BTREE , HASH , FULLTEXT , SPATIAL です。InnoDBテーブルでは通常 BTREE になります。 |
Comment |
インデックス作成時に指定されたコメント。指定されていなければ空文字列です。 |
Index_comment |
インデックス作成時に指定されたコメント。Comment と同じ。 |
3.1.3. 具体的な使用例と出力の解釈
例えば、users
というテーブルがあるとします。このテーブルに id
(主キー), username
(ユニーク), email
, status
, created_at
といったカラムがあり、以下のようなインデックスが定義されていると仮定します。
PRIMARY
(id)idx_username
(username, ユニーク)idx_status_created_at
(status, created_at の複合インデックス)idx_email
(email)
このテーブルに対して SHOW INDEX FROM users;
を実行した出力例を以下に示します。
+--------+------------+---------------------------+-------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index| Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+--------+------------+---------------------------+-------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| users | 0 | PRIMARY | 1 | id | A | 10000 | NULL | NULL | NO | BTREE | | |
| users | 0 | idx_username | 1 | username | A | 10000 | NULL | NULL | NO | BTREE | | |
| users | 1 | idx_status_created_at | 1 | status | A | 10 | NULL | NULL | YES | BTREE | | |
| users | 1 | idx_status_created_at | 2 | created_at | A | 5000 | NULL | NULL | YES | BTREE | | |
| users | 1 | idx_email | 1 | email | A | 9800 | NULL | NULL | YES | BTREE | | |
+--------+------------+---------------------------+-------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.01 sec)
この出力から以下の情報が読み取れます。
PRIMARY
インデックスはNon_unique
が0
であり、ユニークであることがわかります。Key_name
がPRIMARY
で、Column_name
がid
、Seq_in_index
が1
なので、これはid
カラムに対する主キーインデックスです。Null
がNO
なので、id
カラムはNULLを許容しません。idx_username
インデックスもNon_unique
が0
であり、ユニークであることがわかります。Key_name
がidx_username
で、Column_name
がusername
なので、username
カラムに対するユニークインデックスです。idx_status_created_at
は2行に分かれています。これは複合インデックスであり、Key_name
は同じidx_status_created_at
です。Seq_in_index
が1
の行はstatus
カラム、Seq_in_index
が2
の行はcreated_at
カラムを示しています。つまり、このインデックスは(status, created_at)
の順序で構成されています。Non_unique
が1
なので、この複合インデックスの値の組み合わせはユニークである必要はありません。Null
がYES
なので、これらのカラムはNULLを許容します。idx_email
はemail
カラムに対する単一インデックスです。Non_unique
が1
なのでユニークインデックスではありません。Cardinality
は各インデックスまたはインデックス内のカラムのユニークな値の推定数です。例えばid
やusername
はユニークなのでカーディナリティがテーブルの行数(推定10000)に近い値になっています。status
はおそらく種類が少ない(推定10)ためカーディナリティが低くなっています。複合インデックスidx_status_created_at
のcreated_at
のカーディナリティはstatus
よりも高くなっています。カーディナリティはインデックスの選択性を判断する重要な指標となります。カーディナリティが低いカラム(値の種類が少ないカラム、例:性別、フラグ値など)に対する単一インデックスは、検索条件をあまり絞り込めないため、効果が薄いことが多いです。- すべてのインデックスの
Index_type
はBTREE
です。
SHOW INDEX
コマンドは、テーブルに「どのようなインデックスが、どのように定義されているか」を静的に確認するのに非常に役立ちます。
3.2. SHOW CREATE TABLE
コマンド
SHOW CREATE TABLE
コマンドは、指定したテーブルを作成するための CREATE TABLE
ステートメントを表示します。この出力には、テーブルのカラム定義だけでなく、主キー、ユニークキー、通常のインデックスなどの定義も含まれます。
3.2.1. 構文
sql
SHOW CREATE TABLE table_name;
3.2.2. 出力の確認方法
SHOW CREATE TABLE
コマンドの出力は通常2つのカラムを持ちます。1つ目はテーブル名、2つ目は CREATE TABLE
ステートメントのテキストです。インデックス定義は、このテキストカラムの中に INDEX
, UNIQUE KEY
, PRIMARY KEY
といったキーワードで記述されています。
例:SHOW CREATE TABLE users;
の出力例(一部抜粋)
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| users | CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`email` varchar(100) DEFAULT NULL,
`status` enum('active','inactive','deleted') DEFAULT 'active',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`),
KEY `idx_status_created_at` (`status`,`created_at`),
KEY `idx_email` (`email`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
3.2.3. インデックス定義の読み方
出力された CREATE TABLE
文の中のインデックス定義部分を読み解きます。
PRIMARY KEY (\
id`):
idカラムに対する主キー定義です。これは自動的に
PRIMARY` という名前のB-treeインデックスとして作成されます。UNIQUE KEY \
idx_username` (`username`):
usernameカラムに対するユニークキー定義です。
idx_username` という名前のユニークB-treeインデックスとして作成されます。KEY \
idx_status_created_at` (`status`,`created_at`):
statusカラムと
created_atカラムに対する複合インデックス定義です。
idx_status_created_atという名前の非ユニークB-treeインデックスとして作成されます。複合インデックスのカラム順序は定義された順序
(status, created_at)` になります。KEY \
idx_email` (`email`):
emailカラムに対する単一インデックス定義です。
idx_email` という名前の非ユニークB-treeインデックスとして作成されます。KEY \
idx_created_at` (`created_at`):
created_atカラムに対する単一インデックス定義です。
idx_created_at` という名前の非ユニークB-treeインデックスとして作成されます。
SHOW CREATE TABLE
は、テーブル構造全体と合わせてインデックス定義を確認したい場合に便利です。特に、インデックスの名前、含まれるカラムとその順序、ユニーク性などが一目で確認できます。SHOW INDEX
と組み合わせて使うことで、より詳細な情報を把握できます。
4. クエリにおけるインデックス使用状況の確認方法:EXPLAIN
を使いこなす
テーブルにどのようなインデックスが存在するかを知ることは重要ですが、それ以上に重要なのは「特定のクエリを実行したときに、データベースが実際にどのインデックスをどのように使っているか」を理解することです。これを調べるために使うのが EXPLAIN
コマンドです。EXPLAIN
はMySQLのオプティマイザが生成するクエリの実行計画を表示します。
4.1. EXPLAIN
の重要性
- インデックスの有効性の確認: 作成したインデックスが期待通りにクエリで使用されているかを確認できます。
- パフォーマンス問題の原因特定: クエリが遅い場合、
EXPLAIN
を見ることで、インデックスが使われていない、不適切なインデックスが使われている、フルテーブルスキャンが発生している、といった原因を特定できます。 - インデックス設計の検証: 新しいインデックスを作成する前に、
EXPLAIN
を使って「もしこのインデックスがあったら、クエリの実行計画はどう変わるか?」をシミュレーションできます(ただし、実際にインデックスを作成して試すのが最も確実です)。 - クエリのリファクタリング: クエリの書き方を変えることで、より効率的なインデックスの使用を促せる場合があります。
EXPLAIN
を使って、クエリ変更前後の実行計画を比較することで、その効果を確認できます。
4.2. EXPLAIN
の基本的な使い方
使い方は非常に簡単です。調べたい SELECT
クエリの先頭に EXPLAIN
を付け加えるだけです。
sql
EXPLAIN SELECT column1, column2 FROM table_name WHERE condition ORDER BY column3;
EXPLAIN
は SELECT
ステートメントだけでなく、INSERT
, UPDATE
, DELETE
ステートメントに対しても使用できます(ただし、MySQL 5.6以降)。これらのDML文の場合、EXPLAIN
はどのようにデータを取得(WHERE句の条件評価など)するかの計画を表示します。
4.3. EXPLAIN
出力カラムの徹底解説
EXPLAIN
の出力は、クエリに関与するテーブルごとに1行(または複数行)表示され、いくつかのカラムが含まれます。これらのカラムの意味を深く理解することが、実行計画を正しく読み解く鍵となります。
以下に主要なカラムとその意味を詳しく解説します。
4.3.1. id
, select_type
, table
, partitions
id
:
SELECT クエリ内の各操作(テーブルスキャン、結合など)に割り当てられる識別子です。複数のテーブルが結合されている複雑なクエリや、サブクエリを含むクエリでは、複数のid
が出現します。id
が大きいものから順に実行される傾向がありますが、必ずしもそうとは限りません。結合の場合は、同じid
の行が複数出現し、それらはまとめて処理されるグループを示します。select_type
:
SELECT クエリの種類を示します。代表的な値をいくつか挙げます。SIMPLE
: 複雑なクエリではない、単純な SELECT です。サブクエリやUNIONを含みません。PRIMARY
: 最も外側の SELECT です。SUBQUERY
:FROM
句以外の場所にあるサブクエリです(例:WHERE col IN (SELECT ...)
)。DEPENDENT SUBQUERY
: 外側のクエリに依存するサブクエリです。通常、非依存サブクエリより効率が悪いです。DERIVED
:FROM
句にあるサブクエリの結果を一時テーブルとして扱います(派生テーブル)。UNION
:UNION
の2番目以降の SELECT です。DEPENDENT UNION
: 外側のクエリに依存するUNION
の2番目以降の SELECT です。UNION RESULT
:UNION
の結果を格納する一時テーブルです。
table
:
操作対象となっているテーブルの名前です。エイリアスを使っている場合はエイリアス名が表示されます。<derivedN>
のような形式は、id
が N の派生テーブル(FROM
句のサブクエリの結果)を示します。partitions
:
対象となるパーティション。パーティショニングを使用していない場合はNULL
です。パーティショニングを使用している場合、どのパーティションがスキャンされるかを示します。
4.3.2. type
: アクセスタイプを理解する
type
カラムは、MySQLがテーブルからどのように行を取得するか(アクセスタイプ)を示します。これは EXPLAIN
の出力の中で最も重要かつ頻繁に確認すべきカラムの一つです。上から順に、一般的にパフォーマンスが良いアクセスタイプです。
system
: テーブルが1行しか含まない(システムテーブルなど)。これはconst
の特殊なケースです。const
: クエリがPRIMARY KEY
またはUNIQUE INDEX
を定数条件で検索し、最大1行しか返さないことが確実な場合。非常に高速です。例:WHERE id = 1
(idが主キーの場合)。eq_ref
: JOINにおいて、前のテーブルから得られた値を使用して、現在のテーブルのPRIMARY KEY
またはUNIQUE INDEX
を検索し、常に1行だけを返す場合。非常に高速な結合です。ref
: JOINまたは単一テーブルのクエリにおいて、インデックスのプレフィックスではないカラムに対して等価検索(=)を行う場合。または、PRIMARY KEY
やUNIQUE INDEX
でないインデックスに対して等価検索を行う場合。条件にマッチする行が複数存在する可能性があります。eq_ref
よりは遅いですが、十分に高速です。fulltext
: 全文検索インデックス(FULLTEXT index)を使用している場合。ref_or_null
:ref
と同様にインデックスを使って検索しますが、NULL
値も含む条件の場合。例:WHERE column = value OR column IS NULL
index_merge
: 複数のインデックスを組み合わせて使用し、結果をマージして行を取得する場合。例えば、WHERE condition1 OR condition2
の場合に、condition1
に使うインデックスとcondition2
に使うインデックスを両方利用し、結果を結合する。unique_subquery
:IN
句のサブクエリがPRIMARY KEY
またはUNIQUE INDEX
を使用し、重複のない結果を返すことが保証される場合。index_subquery
:IN
句のサブクエリが非ユニークインデックスを使用する場合。range
: インデックスを使用して、ある範囲の値の行を検索する場合。例:WHERE column BETWEEN value1 AND value2
,WHERE column > value
,WHERE column < value
,WHERE column IN (value1, value2, ...)
など。これも比較的効率的です。index
: インデックス全体をスキャンして行を取得する場合。データ行へのアクセスなしに、インデックス自身のデータ(インデックスに含まれるカラムの値)だけでクエリを完結できる「カバリングインデックス(Covering Index)」の場合や、ORDER BY
やGROUP BY
を処理するためにインデックスの順序を利用する場合などに発生します。ALL
よりは速いですが、テーブル全体のスキャンに匹敵するコストがかかる場合もあります(特にインデックスが大きい場合)。ALL
: テーブル全体をスキャンして行を取得する場合(フルテーブルスキャン)。これは最も効率が悪いアクセスタイプであり、パフォーマンス問題の典型的な原因です。 大量のデータを持つテーブルでtype: ALL
となっているクエリは、インデックスの使用を検討すべきです。
パフォーマンスの良い順序(目安): system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
理想的には const
, eq_ref
, ref
, range
といったインデックスを利用したアクセスタイプになっていることを目指します。特に ALL
は避けるべきです。
4.3.3. possible_keys
と key
: 使用可能なインデックスと実際に選ばれたインデックス
possible_keys
:
クエリのWHERE
句やJOIN
句などに指定された条件を満たすために使用「できる可能性のある」インデックスのリストです。MySQLのオプティマイザが候補として考慮したインデックスが表示されます。たとえここに表示されていても、実際に使用されるとは限りません。key
:
MySQLのオプティマイザが、実際に実行計画で「使用すると判断した」インデックスの名前です。possible_keys
にリストされていても、key
がNULL
の場合、オプティマイザはそのクエリではどのインデックスも使用しないと判断したことを意味します。key
に表示されているインデックス名が、そのクエリのパフォーマンスに直接的な影響を与えます。
possible_keys
に表示されているのに key
が NULL
の場合、オプティマイザがインデックスを使わない方が効率的だと判断したか、インデックスが条件に合致していないか、統計情報が古いなどの原因が考えられます。
4.3.4. key_len
: 使用されたインデックスの長さ
実際に使用されたインデックスキーの長さ(バイト単位)です。複合インデックスの場合、どのカラムまでがインデックスとして効果的に使用されたかを知る手がかりになります。例えば、idx_status_created_at
(status
, created_at
) という複合インデックスがあり、クエリが WHERE status = 'active'
のみの条件の場合、key_len
は status
カラムのインデックス長のみを示す可能性があります。もしクエリが WHERE status = 'active' AND created_at > '...'
のように両方のカラムを使用する場合、key_len
は status
と created_at
を合わせた長さを示すでしょう。key_len
を見ることで、複合インデックスがどの範囲まで活用されているかを推測できます。
4.3.5. ref
: インデックス参照に使用されたカラムまたは定数
key
に示されたインデックスを検索するために使用された値を示します。
* const
: 定数(リテラル値)を使ってインデックス検索が行われた場合。
* テーブル名.
カラム名: 別のテーブルのカラムの値を使って結合(JOIN)のインデックス検索が行われた場合。
* func
: 関数を使ってインデックス検索が行われた場合(ただし、関数によってインデックスが無効化されることが多いので注意)。
4.3.6. rows
: 検査される推定行数
クエリを実行するために、対象のテーブルから読み込まれると推定される行数です。これはオプティマイザが統計情報に基づいて計算した値であり、正確な行数ではありませんが、クエリのコストを評価する上で非常に重要な指標です。rows
の値が小さいほど、通常は効率的なクエリと言えます。特に type: ALL
の場合の rows
の値が大きい場合(テーブルの全行数に近い場合)、パフォーマンス問題が発生している可能性が高いです。インデックスが効果的に使われると、rows
の値は大幅に減少します。
4.3.7. filtered
: 条件によってフィルタリングされる行の割合
MySQL 5.1以降で EXPLAIN EXTENDED
またはMySQL 5.7以降のデフォルトの EXPLAIN
に表示されるカラムです。rows
で読み込まれた行のうち、WHERE
句の条件などによって最終的にフィルタリングされる行の割合(パーセンテージ)を示します。例えば、rows
が 10000 で filtered
が 10.00% なら、最終的に約 1000 行が残ることを示します。filtered
の値が低いのに rows
が高い場合、多くの行を読み込んだものの、ほとんどが捨てられている(インデックスで十分に絞り込めていない)状況を示唆します。
4.3.8. Extra
: クエリ実行に関する追加情報
Extra
カラムは、クエリの実行に関する追加的な詳細情報を提供します。ここに表示される内容は非常に多様で、クエリの効率を判断する上で type
と並んで非常に重要です。代表的な値をいくつか挙げます。
Using index
: カバリングインデックス が使用されていることを示します。クエリが必要とするすべてのカラムの値が、インデックス自身に含まれているため、データ行(クラスタ化インデックス)へのアクセスが不要であることを意味します。これは非常に効率的です。例:SELECT id, username FROM users WHERE username = '...'
というクエリに対して、(username, id)
の複合インデックス(またはusername
のユニークインデックス+主キー)があり、それが使われた場合。Using where
:WHERE
句の条件が、インデックスでカバーされていないカラムに対して評価されている、またはインデックスが利用できない複雑な条件(例: 関数を適用した条件、LIKE '%...'
)が含まれていることを示します。インデックスで絞り込んだ後、さらに追加のフィルタリングが必要であることを意味します。Using temporary
: クエリの実行中に結果セットを保持するための一時テーブルが作成されていることを示します。主にGROUP BY
句やORDER BY
句で、インデックスが利用できない場合に発生します。一時テーブルの作成はディスクI/Oやメモリ使用量を増やすため、パフォーマンス低下の原因となることがあります。可能であれば避けたい状況です。Using filesort
: 結果セットをソートするために、MySQLがファイルソート(メモリ上またはディスク上でのソート)を実行していることを示します。主にORDER BY
句で、適切なインデックスがない場合に発生します。これも一時テーブルと同様にコストの高い操作であり、可能であれば避けたい状況です。ORDER BY
句のカラムにインデックスを作成するか、既存のインデックスの順序を利用できるようクエリやインデックス定義を見直すことで回避できる場合があります。Using join buffer (Block Nested Loop)
など: JOINを実行するためにジョインバッファが使用されていることを示します。NLJ (Nested Loop Join), BNL (Block Nested Loop Join), BKA (Batched Key Access Join) などのアルゴリズム名が表示されることもあります。Distinct
: 重複行を削除するためにDISTINCT
が使用されている場合。Using index for group-by
:GROUP BY
の処理を、インデックスの順序を利用して効率的に行っていることを示します。Using index
と同様に効率的なパターンです。
注目の Extra
値:
* 良い: Using index
, Using index for group-by
* 悪い(改善が必要な可能性が高い): Using temporary
, Using filesort
, ALL
と併用される Using where
4.4. EXPLAIN
の応用(EXTENDED
, FORMAT=JSON
)
基本的な EXPLAIN
以外にも、より詳細な情報を得るためのオプションがあります。
EXPLAIN EXTENDED
: (MySQL 5.7.3より前のバージョンでよく使われた)
EXPLAIN
の結果に加えて、filtered
カラムなどを表示します。また、SHOW WARNINGS
を続けて実行すると、MySQLがクエリをどのように書き換えて(最適化して)実行しようとしているかを確認できます。EXPLAIN FORMAT=JSON
: (MySQL 5.6以降)
EXPLAIN
の結果をJSON形式で出力します。これは機械処理や、より詳細な情報(推定コスト、使用されるイテレータなど)の分析に適しています。非常に多くの情報が含まれるため、慣れるまで読み解くのが難しいかもしれません。
通常は基本的な EXPLAIN
コマンドで十分な情報が得られますが、より深く分析したい場合や、なぜオプティマイザが特定の実行計画を選択したのか理解したい場合にこれらのオプションが役立ちます。
4.5. EXPLAIN
結果からインデックス使用を評価する方法
EXPLAIN
の出力を見たら、以下の点をチェックしてインデックスの使用状況を評価しましょう。
type
カラム:ALL
になっていないか?ALL
ならフルテーブルスキャンです。テーブルサイズが大きい場合はパフォーマンス問題の原因になります。- 理想的な
const
,eq_ref
,ref
,range
になっているか? index
になっている場合、それが意図したカバリングインデックスやソートのためのインデックススキャンか? それとも単なる全インデックススキャンで非効率になっていないか?
key
カラム:NULL
になっていないか?NULL
ならインデックスが使われていません。possible_keys
にリストされているのにkey
がNULL
なら、なぜ使われなかったのか原因を特定する必要があります(WHERE句の条件と不一致、データ型不一致、関数、データ分布、統計情報など)。- 意図したインデックスが使われているか? 使われていないなら、オプティマイザが別のインデックスを選んだ理由を考えます。
rows
カラム:- この推定値は妥当か? テーブルの全行数に対して極端に大きくなっていないか?
rows
が小さいほど効率的です。 - 特に
type: ALL
の場合のrows
が大きい場合は要注意です。
- この推定値は妥当か? テーブルの全行数に対して極端に大きくなっていないか?
Extra
カラム:Using temporary
やUsing filesort
が出ていないか? これらはコストの高い処理です。Using index
が出ているか? カバリングインデックスが使われている場合は非常に効率的です。
これらのポイントをチェックすることで、クエリのパフォーマンスボトルネックがインデックスにあるかどうか、そしてどのようなインデックスを追加・修正・削除すべきかのヒントが得られます。
5. インデックスの統計情報と使用状況の分析
SHOW INDEX
や EXPLAIN
は現在のインデックス定義や特定のクエリ実行計画を調べるのに有効ですが、インデックス全体の統計情報や、どのインデックスが実際にどれだけ使われているか(または使われていないか)を知るための方法もあります。
5.1. インデックスのカーディナリティ (SHOW INDEX Cardinality
) の分析
前述の SHOW INDEX
出力に含まれる Cardinality
カラムは、インデックスの「選択性(selectivity)」を示す推定値です。
- カーディナリティが高い: インデックスに含まれるユニークな値が多い。特定の値を検索したときに、結果が少数に絞り込まれやすい。インデックスの効果が高い可能性が高い。
- カーディナリティが低い: インデックスに含まれるユニークな値が少ない。特定の値を検索しても、結果が多数になってしまい、あまり絞り込めない。単一カラムインデックスとしては効果が薄い可能性が高い。
例えば、性別カラムのような、取りうる値の種類が非常に少ないカラムに対して単一インデックスを作成しても、カーディナリティが低いため、WHERE gender = 'male'
といった検索条件ではインデックスを使わない、あるいは使っても効果が薄いとオプティマイザが判断することが多いです。
ただし、カーディナリティが低いカラムでも、他のカラムとの複合インデックスとして定義することで効果を発揮する場合があります。例えば status
(カーディナリティが低い) と created_at
(カーディナリティが高い) の複合インデックス (status, created_at)
は、WHERE status = 'active' AND created_at > '...'
のようなクエリで効率的に機能します。
Cardinality
の値はあくまで推定値であり、データの更新によって古くなる可能性があります。統計情報を最新の状態に保つためには、定期的に ANALYZE TABLE table_name;
コマンドを実行することが推奨されます。
5.2. ハンドラ統計 (SHOW STATUS LIKE 'Handler%';
) から推測する(補足)
SHOW STATUS LIKE 'Handler%';
コマンドは、ストレージエンジンレベルの各種操作に関するカウンターを表示します。これらのカウンターの値を見ることで、間接的にインデックスの使用状況を推測することができます。
特に注目すべきカウンター:
Handler_read_key
: インデックスを使って特定のキーで読み込みを行った回数。この値が大きいほど、インデックスが頻繁に利用されていることを示唆します。Handler_read_rnd_next
: インデックスを使わずに、次の行を「ランダムな順序」で読み込んだ回数。これはフルテーブルスキャンや、インデックスで絞り込んだ後にさらにデータ行を読み込む際に増加します。Handler_read_key
に比べてHandler_read_rnd_next
が非常に大きい場合、インデックスが十分に活用されていない可能性があります。Handler_read_next
: インデックス順またはデータファイルの順序で、次の行を読み込んだ回数。インデックスレンジスキャンや、インデックスカバースキャン、フルテーブルスキャンなどで増加します。Handler_read_first
: インデックス順またはデータファイルの順序で、最初の行を読み込んだ回数。
これらのカウンターはデータベースシステム全体や特定のコネクションでの累積値として表示されるため、どのテーブルやクエリが原因で値が増加したかを特定するのは難しいです。しかし、システム全体としてインデックスがどの程度使われているか、フルテーブルスキャンがどの程度発生しているか、といった大まかな傾向を把握するのに役立ちます。より詳細な情報は、Performance Schemaやsysスキーマを利用するのが一般的です。
5.3. 使われていないインデックスの特定(Performance Schema / sys
スキーマ)
インデックスは作成するだけでなく、メンテナンスも重要です。特に、一度も使われていないインデックスは、書き込み性能の低下やディスク容量の無駄につながるため、削除を検討すべきです。MySQL 5.6以降で利用できるPerformance SchemaやMySQL 5.7以降で追加されたsysスキーマを利用することで、インデックスの使用状況を詳細に追跡できます。
5.3.1. sys.schema_unused_indexes
ビュー
MySQL 5.7以降にデフォルトで含まれる sys
スキーマには、便利なビューが多く含まれています。その中の一つ、sys.schema_unused_indexes
ビューは、Performance Schema の統計情報に基づいて、サーバー起動以降一度も使用されていない可能性のあるインデックス をリストアップしてくれます。
sql
USE sys;
SELECT * FROM schema_unused_indexes;
出力例:
+---------------+-------------+------------------------+---------------+
| object_schema | object_name | index_name | rows_examined |
+---------------+-------------+------------------------+---------------+
| my_database | users | idx_created_at | 0 |
| my_database | orders | idx_order_total_amount | 0 |
+---------------+-------------+------------------------+---------------+
rows_examined
が 0 となっているインデックスは、Performance Schema が有効になってから一度もそのインデックスが利用されるようなクエリが実行されていないことを意味します。
注意点:
* この情報はPerformance Schemaが有効になっている必要があります。
* サーバー起動からの累積情報です。サーバーを再起動すると情報はリセットされます。したがって、十分な期間(アプリケーションが様々な処理を行う期間)サーバーを稼働させた後に確認する必要があります。
* インデックスが使われるケースには様々なものがあります(検索、ソート、グループ化、カバリングインデックス、UNIQUE制約チェックなど)。Performance Schema はこれらの利用を追跡しますが、特定のクエリで「このインデックスが選ばれたかどうか」という厳密な意味での使用とは異なる場合があります。
* 特に書き込み処理(INSERT, UPDATE, DELETE)でのユニーク制約チェックに使われるユニークインデックスは、SELECT クエリで参照されなくても必須の場合があります。schema_unused_indexes
はSELECTでの利用がなかった場合にリストアップするため、ユニークインデックスだからといって安易に削除すべきではありません。
それでも、このビューは不要なインデックスを特定する上で非常に有用な出発点となります。ここにリストアップされたインデックスについて、本当に使われていないのか、削除しても問題ないかを慎重に検討する価値はあります。
5.3.2. Performance Schema を活用した詳細な追跡
Performance Schema は、MySQLサーバーの様々な活動(SQL実行、I/O、ロックなど)に関する低レベルな情報を提供する強力なツールです。インデックスの使用状況についても、より詳細な追跡が可能です。
特に performance_schema.table_io_waits_summary_by_index_usage
テーブルは、インデックスごとのI/O待機時間統計を提供します。これは、特定のインデックスが読み込み操作(SUM_TIMER_WAIT
が READ
に関するもの)や書き込み操作(SUM_TIMER_WAIT
が WRITE
に関するもの)でどれだけ利用されたかを示します。
“`sql
— Performance Schema を有効にする設定が必要です
— 適切なコンシューマ(consumers)とインストルメント(instruments)を有効にする必要があります
— 通常は performance_schema.setup_consumers, performance_schema.setup_instruments を更新します
USE performance_schema;
SELECT
object_schema,
object_name,
index_name,
count_fetch, — SELECT (行取得) でインデックスが使われた回数
count_insert, — INSERT でインデックスが使われた回数 (ユニークキーチェックなど)
count_update, — UPDATE でインデックスが使われた回数
count_delete — DELETE でインデックスが使われた回数
FROM table_io_waits_summary_by_index_usage
WHERE object_schema = ‘your_database_name’
ORDER BY count_fetch DESC, count_insert DESC, count_update DESC, count_delete DESC;
“`
このテーブルは、サーバー起動以降(または統計情報のリセット以降)、各インデックスが読み書き操作で具体的に何回使われたかを示します。count_fetch
がゼロまたは非常に少ないにも関わらず、そのインデックスがSELECT以外の目的(例えばユニーク制約)で必須でない場合、不要なインデックスである可能性が高いです。
Performance Schema を使いこなすには追加の設定や学習が必要ですが、システムの詳細な挙動を分析したい場合に非常に強力なツールとなります。
6. インデックス確認に基づく最適化の実践
インデックスの確認方法をマスターしたら、次はそれらの情報を元にデータベースのパフォーマンスを最適化する方法を考えましょう。
6.1. 使われていないインデックスの削除
sys.schema_unused_indexes
や performance_schema.table_io_waits_summary_by_index_usage
などを確認し、長期間(例えば数週間や数ヶ月など、アプリケーションの通常稼働サイクルをカバーできる期間)にわたって一度も利用されていないインデックスを特定します。そのインデックスがユニーク制約など、SELECT以外の目的で必須でないことを確認したら、削除を検討します。
sql
DROP INDEX index_name ON table_name;
不要なインデックスを削除することで、INSERT, UPDATE, DELETE 処理の高速化、ディスク容量の削減、オプティマイザが最適なインデックスを選択する際の検討候補を減らすことによるクエリ計画生成時間の短縮といったメリットが得られます。ただし、削除は慎重に行い、念のため削除前にインデックス定義をバックアップしておくと良いでしょう。
6.2. 不足しているインデックスの特定と作成
クエリの実行計画を EXPLAIN
で分析し、特に type: ALL
や Extra: Using filesort
, Using temporary
が頻繁に出現するようなパフォーマンスボトルネックとなっているクエリを特定します。
これらのクエリが WHERE
, JOIN
, ORDER BY
, GROUP BY
句で使用しているカラムを確認し、それらのカラムに適切なインデックスが存在しない場合に、新規インデックスの作成を検討します。
sql
CREATE INDEX index_name ON table_name (column1, column2, ...);
インデックス作成後、再度同じクエリに対して EXPLAIN
を実行し、新しいインデックスが使われているか、type
, rows
, Extra
といった指標が改善されているかを確認します。
6.3. 複合インデックスの設計と確認
複数のカラムを組み合わせて検索、ソート、またはグループ化するクエリが多い場合、複合インデックスが有効です。複合インデックスは、定義されたカラムの順序でデータのソートとフィルタリングを行います。
例えば、WHERE status = 'active' AND created_at > '...' ORDER BY created_at
というクエリがある場合、(status, created_at)
の複合インデックスが非常に有効です。EXPLAIN
で確認する際には、この複合インデックスが key
に選ばれ、type
が range
や ref
となり、Extra
に Using index for group-by
や Using index
(カバリングインデックスとして機能する場合) が表示されることを期待します。
複合インデックスの「先頭カラム」から使用される点(左端プレフィックス特性)を理解することが重要です。(col1, col2, col3)
のインデックスは WHERE col1 = ...
, WHERE col1 = ... AND col2 = ...
, WHERE col1 = ... AND col2 = ... AND col3 = ...
のようなクエリに利用されますが、WHERE col2 = ...
や WHERE col3 = ...
のようなクエリ単体には直接利用されません。
6.4. インデックスヒントの利用と注意点
稀なケースですが、MySQLのオプティマイザが最適なインデックスを選択しない場合があります。このような場合、インデックスヒントを使ってオプティマイザの選択に介入することができます。
sql
SELECT ... FROM table_name USE INDEX (index_name) WHERE ...; -- 特定のインデックスを使用するよう推奨
SELECT ... FROM table_name IGNORE INDEX (index_name) WHERE ...; -- 特定のインデックスを使用しないよう推奨
SELECT ... FROM table_name FORCE INDEX (index_name) WHERE ...; -- 特定のインデックスを強制的に使用
注意点: インデックスヒントの使用は慎重に行うべきです。オプティマイザは統計情報に基づいて最適な判断をしようとしますが、データ分布の変化などでその判断が外れることがあります。ヒントを使うとそのクエリの実行計画は固定されますが、データが変化してもヒントは変わりません。結果として、将来的にパフォーマンスが劣化する可能性があります。基本的にはインデックスヒントに頼らず、オプティマイザが正しいインデックスを選べるよう、インデックス設計や統計情報の更新で対応することを優先すべきです。
6.5. 統計情報の更新 (ANALYZE TABLE
) の重要性
SHOW INDEX
の Cardinality
や EXPLAIN
の rows
, filtered
といった値は、MySQLの統計情報に基づいて計算されます。データが頻繁に更新されるテーブルでは、この統計情報が古くなり、オプティマイザが不適切な実行計画を選択する可能性があります。
定期的に ANALYZE TABLE
コマンドを実行することで、テーブルとインデックスの統計情報を最新の状態に更新できます。
sql
ANALYZE TABLE table_name;
特に、大量のデータが追加・更新・削除された後や、EXPLAIN
結果の rows
や Cardinality
の値が実態と大きくかけ離れているように見える場合に実行することを検討しましょう。
7. 実践ケーススタディ
これまでに学んだ確認方法を使って、実際のパフォーマンス問題を解決するケーススタディを見てみましょう。
7.1. ケース1:フルテーブルスキャンをインデックスで改善する
products
テーブルがあり、name
カラムで商品を検索するクエリが遅いとします。
遅いクエリ:
sql
SELECT * FROM products WHERE name LIKE 'MySQL%';
インデックス確認:
まず SHOW INDEX FROM products;
を実行し、name
カラムにインデックスがあるか確認します。もしインデックスがないか、あっても全文検索インデックスなどで LIKE 'prefix%'
に適していないB-treeインデックスがない場合、インデックス不足が考えられます。
EXPLAIN
で確認:
sql
EXPLAIN SELECT * FROM products WHERE name LIKE 'MySQL%';
出力結果の type
が ALL
になっており、key
が NULL
である可能性が高いです。rows
の値もテーブルの全行数に近いでしょう。Extra
に Using where
が表示されているかもしれません。これはフルテーブルスキャンが行われていることを明確に示しています。
最適化:
name
カラムにB-treeインデックスを作成します。
sql
CREATE INDEX idx_name ON products (name);
再確認:
再度 EXPLAIN SELECT * FROM products WHERE name LIKE 'MySQL%';
を実行します。
出力結果の type
が range
または ref
に、key
が idx_name
になっていることを期待します。rows
の値も大幅に減少しているでしょう。これにより、インデックスが効果的に使用されるようになり、クエリのパフォーマンスが改善されます。
7.2. ケース2:複合インデックスの効果を EXPLAIN
で確認する
orders
テーブルに status
と order_date
カラムがあり、「特定のステータスの注文を日付範囲で検索する」クエリが遅いとします。
クエリ:
sql
SELECT * FROM orders WHERE status = 'completed' AND order_date BETWEEN '2023-01-01' AND '2023-12-31';
インデックス確認:
SHOW INDEX FROM orders;
でインデックスを確認します。status
単一、order_date
単一、または (status, order_date)
以外の複合インデックスが存在するかもしれません。
EXPLAIN
で確認 (インデックスがないか、不適切な場合):
EXPLAIN SELECT ...
の出力が type: ALL
か、あるいは status
か order_date
のどちらかの単一インデックスを使っていても rows
がまだ大きい、Extra: Using where
が出ている、といった状況が考えられます。
最適化:
このクエリは status
と order_date
の両方でフィルタリングしています。status
はカーディナリティが低い可能性が高いですが、order_date
と組み合わせることで効果的な検索が期待できます。複合インデックス (status, order_date)
を作成します。
sql
CREATE INDEX idx_status_date ON orders (status, order_date);
status
を先に定義するのは、等価検索(=
)で利用され、その後の order_date
の範囲検索(BETWEEN
)でインデックスの左端プレフィックス特性を最大限に活かすためです。
再確認:
再度 EXPLAIN SELECT ...
を実行します。
出力結果の key
が idx_status_date
に、type
が range
になっていることを期待します。rows
の値が大幅に減少し、効率的なレンジスキャンが行われていることがわかります。
7.3. ケース3:不要なインデックスを特定・削除する
サーバーを数ヶ月稼働させた後、特定のテーブルの書き込み処理(INSERTなど)が遅くなってきたと感じています。
インデックス確認:
まず SHOW INDEX FROM your_table;
でテーブルに多くのインデックスが定義されていることを確認したとします。
不要インデックスの特定:
sys.schema_unused_indexes
ビューを確認します(Performance Schema が有効である前提)。
sql
USE sys;
SELECT * FROM schema_unused_indexes WHERE object_schema = 'your_database_name' AND object_name = 'your_table';
この出力で、例えば idx_old_flag_column
の rows_examined
が 0 になっているインデックスが見つかったとします。
追加調査:
本当にこのインデックスが不要か慎重に判断します。
* そのカラムは現在のアプリケーションで検索条件やソート条件として使われているか? コード grep やクエリログ調査などで確認します。
* そのインデックスはユニークインデックスか? ユニーク制約のために必要か?
* Performance Schema の詳細な統計情報 (performance_schema.table_io_waits_summary_by_index_usage
) も確認し、count_fetch
がゼロに近いか、他の操作での利用もないか確認します。
最適化:
idx_old_flag_column
インデックスが SELECT、ユニーク制約、その他の目的でも長期間全く使われていないと判断した場合、削除を検討します。
sql
DROP INDEX idx_old_flag_column ON your_table;
インデックス削除後、書き込み性能が改善されたか、及び削除したインデックスに依存していた潜在的なクエリがないか(通常は削除前に確認すべきですが)を監視します。
8. まとめ:インデックス確認のマスターへの道
MySQLのパフォーマンス最適化において、インデックスの管理は中心的な役割を果たします。そして、適切なインデックス管理の第一歩は、インデックスの「確認」にあります。本記事では、インデックスの定義から、クエリでの使用状況、さらには利用されていないインデックスの特定まで、様々な確認方法を詳細に解説しました。
今回学んだ主要なコマンドとツールを改めて振り返りましょう。
SHOW INDEX FROM table_name;
: テーブルに存在するインデックスの一覧と、各インデックスの構成、ユニーク性、カーディナリティといった静的な詳細情報を確認する。SHOW CREATE TABLE table_name;
: テーブル定義全体の中で、インデックスがどのように定義されているかを確認する。EXPLAIN SELECT ...;
: 特定のクエリが実行される際の計画、特にどのインデックスが使われるか、どのようにデータにアクセスするか (type
)、推定されるコスト (rows
)、その他の重要な情報 (Extra
) を確認する。これはクエリのパフォーマンス問題のデバッグに最も強力なツール。sys.schema_unused_indexes
(MySQL 5.7+): サーバー起動以降、使用されていない可能性のあるインデックスを特定する。不要なインデックスの整理に役立つ。- Performance Schema (e.g.,
table_io_waits_summary_by_index_usage
): より詳細なインデックスごとの利用統計(読み書き回数など)を確認し、インデックスの使用状況を深く分析する。 ANALYZE TABLE table_name;
: インデックスの統計情報を更新し、オプティマイザがより正確な実行計画を立てられるようにする。
これらのツールを状況に応じて使い分けることで、「これで安心!」と言えるレベルでMySQLのインデックス状況を把握し、パフォーマンス改善のための具体的なアクション(インデックスの作成、修正、削除)に繋げることができます。
インデックスの最適化は一度行えば終わりではありません。アプリケーションの進化、データ量の増加、クエリパターンの変化などに伴い、インデックスの効果も変化します。定期的にインデックスの使用状況を確認し、クエリの実行計画を分析し、必要に応じてインデックス戦略を見直すという継続的なプロセスが重要です。
この記事が、あなたがMySQLのインデックス確認と最適化のスキルをマスターするための一助となれば幸いです。さらに深く学ぶためには、MySQL公式ドキュメントの「Optimizing Queries with EXPLAIN」やPerformance Schemaに関する章を参照したり、より複雑なインデックス設計パターン(カバリングインデックスの詳細、特定のデータ型へのインデックスなど)について学習したりすることをお勧めします。
データベースのパフォーマンスは、快適なアプリケーション体験の基盤です。インデックスを適切に管理し、ユーザーに高速なサービスを提供できるよう、今日からインデックス確認を実践していきましょう。