みなさん、こんにちは。
Webアプリケーションを開発・運用していると、「なんだか最近、ページの表示が遅いな…」「特定の機能を使うと、待たされることが多い」といった問い合わせが来ることがありますよね。先日私も、開発中のWebアプリのダッシュボード表示が遅いという問い合わせを受けました。
調査の結果、その原因はダッシュボードのデータ取得に時間がかかっていることだと判明。詳しく調べてみると、データ取得に使われているSQLクエリに問題がありました。特に、必要以上に大量のデータを取得していることと、ORDER BY
句で使っているカラムにインデックスがないことがパフォーマンスのボトルネックになっていたのです。
これらの問題を解決するため、SQLクエリの見直しとデータベースのチューニングを実施。その結果、データ取得速度が劇的に向上し、ダッシュボードはサクサク表示されるようになりました。
データベースのパフォーマンスチューニングにおいて、インデックスの追加はまさに「定番」とも言える有効な手段です。しかし、やみくもにインデックスを追加すれば良いというものではありません。むしろ、使い方を間違えると逆効果になることさえあります。
そこで今回は、「どんな時にインデックスを作成すべきか?」という、多くの開発者が悩むポイントについて、具体的な状況を交えながら解説していきたいと思います。今回はデータベースとしてPostgreSQLとMS SQL Serverを対象にしますが、どのデータベースを使っているとしても考え方は一緒ですので役立つヒントがきっと見つかるはずです!
【注意】
本記事で解説するインデックスの基本的な概念やチューニングの考え方は、PostgreSQLおよびMS SQL Server(PostgreSQL 9.x以降、MS SQL Server 2008 R2以降を想定)で共通して有効です。ただし、一部の機能(例えばMS SQL Serverの列ストアインデックスなど)は、より新しいバージョンで導入または強化されています。各データベースの最新版(2025年7月現在、PostgreSQL 17、SQL Server 2022)でも、これらの基本的な考え方やツールは引き続き利用できます。
インデックス作成を検討すべき5つのケース
データベースのパフォーマンス低下に直面したとき、インデックスの追加は非常に強力な解決策となり得ます。しかし、インデックスは諸刃の剣でもあるため、その必要性を慎重に見極めることが重要です。ここでは、インデックス作成を検討すべき具体的な5つのケースをご紹介します。
1. 特定の SELECT クエリが極端に遅いとき
最も一般的なケースは、特定のデータ取得(SELECT
)クエリの実行速度が遅い場合です。これは、データベースが目的のデータを見つけるためにテーブル全体を読み込んでいる(フルスキャンしている)可能性が高いことを示唆しています。
- WHERE 句での条件指定
- クエリの
WHERE
句で頻繁に使われるカラムにインデックスがないと、データベースは条件に合致する行を探すためにすべての行をチェックしなければなりません。
例えば、SELECT * FROM users WHERE email = 'test@example.com';
のようなクエリで、email
カラムにインデックスがない場合、ユーザー数が多ければ多いほど検索に時間がかかります。
- クエリの
- JOIN 操作のキー
- 複数のテーブルを結合する
JOIN
クエリで、結合キーとなるカラムにインデックスがないと、結合処理が非効率になり、大幅なパフォーマンス低下を招きます。
- 複数のテーブルを結合する
- ORDER BY 句と GROUP BY 句
- データを特定の順序で並べ替えたり(
ORDER BY
)、グループ化したり(GROUP BY
)する際に、対象のカラムにインデックスがあると、データベースはソート処理やグループ化処理をより効率的に行うことができます。特に、インデックスの格納順序が要求されるソート順序と一致している場合、ソート処理自体をスキップできるため、劇的な速度向上が見込めます。
- データを特定の順序で並べ替えたり(
- DISTINCT 操作
- 重複する値を除外してユニークな値を抽出する
DISTINCT
操作も、対象カラムにインデックスがあることで処理が高速化されることがあります。
- 重複する値を除外してユニークな値を抽出する
2. テーブルのデータ量が非常に多いとき
テーブルに格納されているデータが大量になればなるほど、インデックスの恩恵は大きくなります。
少量のデータではフルスキャンでも問題にならないことがありますが、数百万、数千万といったレコードになると、インデックスの有無がクエリの実行時間に雲泥の差を生み出します。大量データの中から特定のレコードを探す「図書館で本を探す」ような状況を想像してみてください。タイトルや著者名で分類された目録(インデックス)がなければ、一冊一冊すべての本をめくって探すしかありませんよね。
3. クエリの実行計画で「テーブルスキャン」が頻出するとき
データベースには、クエリの実行計画を表示してくれる便利な機能があります。これを利用して、どこにパフォーマンス上のボトルネックがあるかを確認できます。
- PostgreSQL
EXPLAIN
コマンド(特にEXPLAIN ANALYZE
)を使います。実行計画で「Seq Scan」(シーケンシャルスキャン)が頻繁に表示されている場合、インデックスが使われずにテーブル全体を読み込んでいる可能性が高いです。
- MS SQL Server
- SQL Server Management Studio (SSMS) の「実行プランを含める」機能や、
SET SHOWPLAN_ALL ON
、SET STATISTICS PROFILE ON
といったコマンドを使用します。 - 実行プランで「Table Scan」(テーブルスキャン)が大きなコストになっている場合、インデックス作成を検討すべき箇所を示しています。また、SSMSの実行プランは視覚的に分かりやすく、どの操作がボトルネックになっているかを色分けやパーセンテージで表示してくれます。
- SQL Server Management Studio (SSMS) の「実行プランを含める」機能や、
4. FOREIGN KEY 制約のパフォーマンスが懸念されるとき
データベースの参照整合性を保つために設定する FOREIGN KEY
制約。親テーブルのレコードが削除されたり更新されたりする際に、関連する子テーブルのレコードの整合性を確認します。
- PostgreSQL
- 通常、
FOREIGN KEY
制約が設定された子テーブルのカラムに自動的にインデックスを作成しません(または、既存のインデックスを利用しようとします)。しかし、子テーブルから親テーブルへの参照操作が頻繁に行われる場合や、親テーブルの参照される側のカラムにインデックスがない場合、関連するクエリのパフォーマンスが低下することがあります。このようなケースでは、明示的にインデックスを作成することでパフォーマンスを改善できる可能性があります。
- 通常、
- MS SQL Server
FOREIGN KEY
制約は、参照される側のカラム(親テーブルの主キーなど)にインデックスが存在しない場合、パフォーマンス問題を引き起こすことがあります。特にON DELETE CASCADE
やON UPDATE CASCADE
の設定がある場合、親テーブルの変更が子テーブルに伝播する際に、適切なインデックスがないと処理が遅くなることがあります。
5. 特定のデータアクセスパターンが確立されているとき
アプリケーションの要件として、常に同じカラムを基準にデータを検索したり、特定のソート順でデータを取得したりするなど、予測可能なデータアクセスパターンがある場合、そのパターンに最適化されたインデックスを作成することで、継続的にパフォーマンスを向上させることができます。
例えば、ユーザーの最終ログイン日時順に常にリストを表示する必要がある場合、そのタイムスタンプカラムにインデックスを作成することで、ソート処理を効率化できます。
今回の私の場合、5のクエリに1と2の問題が発生していたわけです。とりわけ、比較的頻繁にアクセスする大量のデータのORDER BY
句のカラムにインデックスを追加したのは非常に効果的でした。
インデックス作成の注意点とMS SQL Server特有の考慮事項
インデックスはパフォーマンス改善の強力なツールですが、万能ではありません。「過ぎたるは猶及ばざるが如し」という言葉があるように、不必要なインデックスの作成は、かえってシステム全体のパフォーマンスを悪化させる可能性があります。
- 書き込み性能の低下
- インデックスを作成すると、データの
INSERT
、UPDATE
、DELETE
といった書き込み操作の際に、データベースはインデックス自身も更新する必要があります。そのため、書き込み頻度の高いテーブルに多数のインデックスを作成すると、これらの操作が遅くなる可能性があります。
- インデックスを作成すると、データの
- ディスク容量の消費
- インデックスはそれ自体がデータ構造であり、ディスク容量を消費します。特に、大規模なテーブルに多数のインデックスを作成すると、無視できないディスク容量が必要になります。
- 管理コスト
- インデックスが増えれば増えるほど、データベースの管理が複雑になります。どのインデックスが使われているか、効果があるかなどを定期的に監視し、不要なインデックスは削除するといったメンテナンスも必要になります。
MS SQL Server特有のインデックスの考慮事項
MS SQL Serverには、PostgreSQLにはない独自のインデックスタイプや機能があります。
- クラスター化インデックス (Clustered Index)
- MS SQL Serverでは、テーブルごとに1つだけ作成できる特別なインデックスです。このインデックスは、データ自体を物理的にソートして格納します。
- 検索や範囲指定のクエリで非常に高いパフォーマンスを発揮しますが、作成後にデータの物理的な再配置が発生するため、大規模なテーブルでは慎重に設計する必要があります。主キーにクラスター化インデックスを作成するのが一般的です。
- クラスター化インデックスが定義されていないテーブルは「ヒープテーブル」と呼ばれます。
- 非クラスター化インデックス (Non-Clustered Index)
- PostgreSQLのインデックスに近い概念で、データのコピー(ソートされたキー値と行ポインタ)を作成し、元のデータは元の場所に維持されます。テーブル上に複数作成可能です。
- 付加列 (Included Columns)
- 非クラスター化インデックスに、キー以外のカラムを「付加列」として含めることができます。
- これにより、インデックスがカバーする情報が増え、クエリがテーブル自体にアクセスする(「ブックマークルックアップ」と呼ばれる非効率な処理)回数を減らすことができます。
- 特に
SELECT
句でよく使われるが、キーとしては使われないカラムを含めると効果的です。
- 列ストアインデックス (Columnstore Index)
- 大規模なデータウェアハウスや分析ワークロードに特化したインデックスで、データを列志向で格納します。
- 集計クエリやデータ分析において非常に高いパフォーマンスを発揮します。この機能はSQL Server 2012以降で導入され、SQL Server 2014以降で更新可能なクラスター化列ストアインデックスが登場しました。
- インデックスの断片化
- MS SQL Serverでは、データの更新や削除によってインデックスが断片化することがあります。
- 断片化が進むとパフォーマンスが低下するため、定期的なインデックスの再構成(
ALTER INDEX ... REORGANIZE
)や再構築(ALTER INDEX ... REBUILD
)が必要です。 - PostgreSQLでもVACUUMなどのメンテナンスは重要ですが、MS SQL Serverではより明示的なインデックスメンテナンスが推奨されます。
まとめ
今回は、PostgreSQLとMS SQL Serverの両方でインデックス作成を検討すべきタイミングについて解説しました。データベースのパフォーマンスチューニングにおいて、インデックスは非常に重要な要素です。
- SELECT クエリの遅延
WHERE
、JOIN
、ORDER BY
、GROUP BY
、DISTINCT
などの句で使われるカラムをチェック!
- 大量データ
- データ量が増えるほどインデックスの恩恵も増大。
- 実行計画の確認
- PostgreSQLなら
EXPLAIN
の「Seq Scan」、MS SQL Serverなら「Table Scan」をチェックし、ボトルネックを見つける
- PostgreSQLなら
- FOREIGN KEY 関連
- 必要に応じて明示的なインデックス作成も検討
- 予測可能なアクセスパターン
- 特定の利用パターンに合わせて最適化
PostgreSQLとMS SQL Serverではインデックスの特性やメンテナンス方法に違いがありますが、基本原則は共通しています。闇雲にインデックスを追加するのではなく、「どのクエリが遅いのか?」「どのカラムが頻繁に使われるのか?」といった具体的な状況を把握し、実行計画を分析しながら、本当に必要な場所にインデックスを作成することが重要です。
適切なインデックス戦略で、データベースをより高速に、そして効率的に運用していきましょう!
本日も最後までお読みいただきありがとうございました。
それでは、よいデータベース管理ライフを!