こんにちは、かつコーチです。
SQL基礎編では「インデックスを張ると検索が速くなる」というところまでお話ししました。
ただ、MySQLには用途に応じて何種類かのインデックスが用意されていて、それぞれ得意な検索が違います。
今回は「インデックス・パフォーマンス編」の1本目として、MySQLで使えるインデックスの種類と使い分けを整理していきます。
インデックスとは?改めて整理する
インデックスの役割
インデックスとは、テーブルの特定のカラムに対して作られる「検索を高速化するための索引」です。
本の巻末索引と同じ仕組みで、インデックスがないと1行目から順番に全件を確認するフルテーブルスキャンが発生し、テーブルが大きくなるほど検索が遅くなります。
MySQLのインデックスは1種類ではない
「インデックス」とひとまとめに言われがちですが、MySQLには内部の構造が異なる複数のインデックスがあります。
代表的なのは次の3つです。
- B-Treeインデックス:等号・範囲検索・ソートに強い、最も基本的なインデックス
- フルテキストインデックス:文章中の単語検索(全文検索)に強いインデックス
- 空間インデックス(Spatial Index):緯度経度などの位置情報検索に強いインデックス
それぞれ得意な検索条件が違うため、「何を検索したいか」によって選ぶインデックスが変わってきます。
B-Treeインデックス:基本にして最も使う
B-Treeインデックスの仕組み
B-Tree(Balanced Tree)インデックスは、値を木構造で管理し、根から葉に向かって二分探索的に絞り込んでいくデータ構造です。
InnoDBのデフォルトインデックスであり、PRIMARY KEYやUNIQUE KEY、通常のINDEXはすべてB-Treeインデックスとして作られます。
-- ordersテーブルのcustomer_idにB-Treeインデックスを張る
CREATE INDEX idx_orders_customer_id ON orders (customer_id);
-- 実際に使われる検索例
SELECT * FROM orders WHERE customer_id = 1001;
SELECT * FROM orders WHERE created_at BETWEEN '2026-08-01' AND '2026-08-31';
SELECT * FROM orders ORDER BY created_at DESC LIMIT 10;
B-Treeインデックスは、等号検索(=)・範囲検索(BETWEENや<、>)・並び替え(ORDER BY)のすべてに対応できる汎用性の高さが強みです。
複合インデックスと左端一致の原則
複数のカラムをまとめて1つのインデックスにすることもでき、これを複合インデックスと呼びます。
-- customer_idとstatusの組み合わせでよく検索する場合
CREATE INDEX idx_orders_customer_status ON orders (customer_id, status);
複合インデックスには左端一致の原則があり、インデックスに定義した順番の先頭から条件を指定しないと効率よく使われません。
-- ✅ インデックスが使われる(左端のcustomer_idから条件指定)
SELECT * FROM orders WHERE customer_id = 1001 AND status = 'paid';
SELECT * FROM orders WHERE customer_id = 1001;
-- ❌ インデックスが使われにくい(左端のcustomer_idを飛ばしている)
SELECT * FROM orders WHERE status = 'paid';
statusだけで検索することが多いなら、status単体のインデックスを別に用意するか、カラムの並び順を(status, customer_id)にするかを検討する必要があります。
フルテキストインデックス:文章検索に特化
LIKE検索との違い
商品名やブログ本文のような長い文章から特定の単語を探したい場合、B-Treeインデックスは不得意です。
-- ❌ 前方一致でないLIKE検索はインデックスが使われない
SELECT * FROM articles WHERE body LIKE '%MySQL%';
LIKE '%キーワード%'のように前方が%で始まる検索は、B-Treeインデックスの木構造を辿れないため、結局フルテーブルスキャンになってしまいます。
この課題を解決するのがフルテキストインデックスで、文章をあらかじめ単語単位に分解(トークン化)して索引を作り、単語検索を高速化します。
-- フルテキストインデックスの作成
ALTER TABLE articles ADD FULLTEXT INDEX ft_articles_body (body);
-- MATCH AGAINSTによる全文検索
SELECT id, title
FROM articles
WHERE MATCH(body) AGAINST('MySQL インデックス' IN NATURAL LANGUAGE MODE);
日本語を扱う際の注意点
英語は単語がスペースで区切られているため単純ですが、日本語は単語の区切りがないため、MySQL 8.0ではNgramパーサーという仕組みで対応します。
-- 日本語向けにNgramパーサーを指定して作成する
ALTER TABLE articles ADD FULLTEXT INDEX ft_articles_body_ja (body) WITH PARSER ngram;
Ngramパーサーは文章を2〜3文字ずつの塊に分解して索引化するため、日本語でもある程度の精度で単語検索ができるようになります。
ただし、英語向けの標準パーサーのまま日本語カラムにフルテキストインデックスを張ると、意図した検索結果が返らないので注意してください。
空間インデックス:位置情報検索に特化
どんな場面で使うか
店舗検索アプリのように「現在地から半径○km以内の店舗」を探すような、緯度経度を扱う検索では空間インデックス(Spatial Index)を使います。
-- POINT型のカラムを持つテーブル
CREATE TABLE shops (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
location POINT NOT NULL SRID 4326,
SPATIAL INDEX idx_shops_location (location)
);
-- 指定した範囲内の店舗を検索
SELECT id, name
FROM shops
WHERE ST_Contains(
ST_Buffer(ST_GeomFromText('POINT(139.7671 35.6812)', 4326), 0.05),
location
);
空間インデックスはB-Treeとは異なるR-Treeという構造を使っており、2次元・3次元の座標データに対する範囲検索を高速化します。
初中級の実務では出番が少ないインデックスですが、店舗検索や配送エリア判定などの機能を実装する際には必須の知識になります。
どのインデックスを選ぶべきか
選択の判断軸
| インデックス種類 | 得意な検索 | 主な用途 |
|---|---|---|
| B-Tree | 等号・範囲・ソート | 通常のWHERE句、ORDER BY全般 |
| フルテキスト | 文章中の単語検索 | ブログ本文検索、商品名検索 |
| 空間インデックス | 位置情報の範囲検索 | 店舗検索、配送エリア判定 |
迷ったときは、まずB-Treeインデックスで対応できないかを検討し、LIKE '%〜%'のような部分一致検索が必要になった時点でフルテキストインデックスを検討する、という順番がおすすめです。
私がインデックスの種類を意識せずハマった話
以前、記事検索機能を作った際に、本文カラムにLIKE '%キーワード%'をそのまま使ってしまい、記事数が1,000件を超えたあたりから検索に3秒近くかかるようになったことがあります。
原因を調べようとしたところ、EXPLAINの結果にtype: ALL(フルテーブルスキャン)と表示されており、B-Treeインデックスを張っていたにもかかわらず全く使われていませんでした。
フルテキストインデックスに切り替えてMATCH AGAINSTで検索するように修正したところ、同じ検索が0.1秒以下で返るようになり、「インデックスは種類まで意識しないと意味がない」ことを痛感した経験です。
まとめ
この記事のポイント
- MySQLにはB-Tree・フルテキスト・空間インデックスの3種類があり、得意な検索がそれぞれ異なる
- B-Treeインデックスは等号・範囲検索・ソートに強く、複合インデックスは左端一致の原則を意識する
- 文章の部分一致検索にはフルテキストインデックスを使い、日本語にはNgramパーサーを指定する
- 位置情報の範囲検索には空間インデックス(R-Tree構造)を使う
- LIKE ‘%〜%’のような部分一致検索は、B-Treeインデックスが使われずフルテーブルスキャンになりやすい
次に読むべき記事
次回は「EXPLAINでMySQLの実行計画を読む」です。
今回登場したtype: ALLのように、インデックスが実際に使われているかを確認する方法を詳しく解説していきます。
タグ: MySQL, 中級者向け, パフォーマンス