2025年10月23日木曜日

SQL Server インデックスの仕組みと最適化|種類・構造・実務チューニング解説

SQL Server 解説用イメージ

目次

インデックスとは

インデックスの基本概念と役割

インデックスとは、SQL Serverでデータ検索を高速化するための仕組みです。
書籍の索引や図書館の分類表のように、検索対象となる列に「目次」を作り、目的の行を素早く見つけられるようにします。
インデックスを使うことで、テーブル全体をスキャンする必要がなくなり、特に大規模データでの検索効率が大幅に改善されます。

テーブルスキャンとの違い

インデックスが存在しない場合、SQL Serverは条件に一致する行を探すためにテーブルのすべての行を読み込みます。
これをテーブルスキャンと呼びます。
一方、インデックスがある場合は、指定列に基づいて検索範囲を絞り込むインデックスシークが可能となり、I/Oを最小限に抑えられます。
ただし、INSERT・UPDATE・DELETE時にはインデックスも更新されるため、過剰な定義はパフォーマンスを下げる要因になります。

インデックスの種類

クラスタ化インデックス(Clustered Index)

クラスタ化インデックスは、テーブル内のデータ行そのものを並び替えて保持するインデックスです。
1つのテーブルに1つしか作成できず、データはインデックスの順序に沿って物理的に格納されます。

CREATE CLUSTERED INDEX IX_Sales_SaleDate
ON dbo.Sales(SaleDate);

クラスタ化インデックスを持つテーブルは「ヒープ構造」ではなく、行がインデックスキー順に整理された状態になります。
範囲検索やORDER BYに強く、主キーに設定されることが一般的です。

非クラスタ化インデックス(Nonclustered Index)

非クラスタ化インデックスは、データとは別の領域に作成され、実データ行へのポインタを保持します。
複数作成可能で、頻繁に検索条件に使う列に設定すると効果的です。

CREATE NONCLUSTERED INDEX IX_Sales_CustomerId
ON dbo.Sales(CustomerId);

非クラスタ化インデックスでは、必要な列がインデックス内にすべて含まれていない場合、キー・ルックアップ(基データ参照)が発生します。
これを防ぐためには、INCLUDE句を使ってカバーリングインデックスを作成します。

CREATE NONCLUSTERED INDEX IX_Sales_CustomerId
ON dbo.Sales(CustomerId)
INCLUDE (SaleDate, Amount);

その他のインデックス(ユニーク・複合・フィルター付き)

SQL Serverでは、用途に応じた派生的なインデックスも利用可能です。

  • ユニークインデックス: 重複を防止し、一意性を保証します。
  • 複合インデックス: 複数列をまとめて検索性能を向上。
  • フィルター付きインデックス: 条件付きで部分的なデータにインデックスを適用。
CREATE UNIQUE INDEX IX_Users_Email
ON dbo.Users(Email);

CREATE INDEX IX_Sales_Region_Amount
ON dbo.Sales(Region, Amount);

CREATE INDEX IX_Sales_Active
ON dbo.Sales(SaleDate)
WHERE IsActive = 1;

インデックスの作成と削除

CREATE INDEXの基本構文

インデックス作成は簡単で、1行のコマンドで実行できます。
ただし、既存データ量が多い場合は作成に時間がかかるため、業務影響を考慮してスケジュールを組みましょう。

CREATE NONCLUSTERED INDEX IX_Orders_Status
ON dbo.Orders(Status);

DROP INDEXとALTER INDEXの使い方

不要なインデックスはDROPで削除できます。

DROP INDEX IX_Orders_Status ON dbo.Orders;

また、既存のインデックスを再構築・再編成する場合はALTER INDEXを使用します。

ALTER INDEX IX_Orders_Status ON dbo.Orders REBUILD;
ALTER INDEX IX_Orders_Status ON dbo.Orders REORGANIZE;

REBUILDは完全再構築(I/O多め)、REORGANIZEは軽い断片化解消です。

インデックスの内部構造と動作

Bツリー構造の概要

SQL ServerのインデックスはBツリー構造で構成されています。
ルートノード、中間ノード、リーフノードの3階層で、キー値を辿りながらデータ行を効率的に特定します。

クラスタ化インデックスのリーフは実データを持ち、非クラスタ化インデックスのリーフはポインタ(RIDまたはクラスタキー)を保持します。

シーク/スキャン/ルックアップの違い

実行計画上でインデックスの動作を確認する際、以下の用語が登場します。

  • Index Seek: 条件を利用して効率的に絞り込み検索
  • Index Scan: インデックス全体を走査(範囲が広い場合)
  • Key Lookup: 非クラスタ化から実データを参照する動作

これらの動きを把握することで、クエリチューニング時にどの列にインデックスを付けるべきかが判断しやすくなります。

パフォーマンス最適化の実践ポイント

実行計画の読み方(Index Seek/Scan)

SQL Server Management Studio(SSMS)で「実行計画を表示」すると、クエリがインデックスを使っているか確認できます。
理想はIndex Seekですが、条件式に関数を使うとインデックスが無効になることもあります。

-- インデックスが使われない例
WHERE YEAR(SaleDate) = 2025;

-- インデックスが使われる例
WHERE SaleDate BETWEEN '2025-01-01' AND '2025-12-31';

このように、関数を避けた記述やSARGable(Search ARGument able)な条件を意識することが重要です。

過剰インデックスと断片化対策

インデックスを作りすぎると、更新コストが増え、INSERT/UPDATE/DELETEが遅くなります。
用途に合わせて必要最低限の定義を維持するのが基本です。
また、断片化が進むと読み取り効率が下がるため、定期的なREBUILD/REORGANIZEが推奨されます。

インデックスメンテナンス

統計情報の更新と再構築

SQL Serverはクエリ最適化に統計情報を利用しています。
古い統計は誤った実行計画を招くため、定期的に更新する必要があります。

UPDATE STATISTICS dbo.Sales;
EXEC sp_updatestats;

REORGANIZE/REBUILD の使い分け

断片化率に応じて以下のように使い分けます。

  • 10〜30%未満 → REORGANIZE(軽めの再編成)
  • 30%以上 → REBUILD(完全再構築)

夜間バッチやメンテナンスウィンドウで定期実行することで、性能を安定させられます。

まとめと実務での指針

インデックスはSQL Serverのパフォーマンスを支える基盤です。
クラスタ化・非クラスタ化の特性を理解し、実行計画を確認しながら設計・維持することが重要です。
適切な数と構造を維持し、断片化を防ぐことで、クエリの応答速度とシステム安定性が向上します。

参考リンク(Microsoft公式ドキュメント)

SQL Server 解説用イメージ

0 件のコメント:

コメントを投稿