はじめに
こんにちは。アメリカで独学でエンジニアを目指している者です。
現在 Rails を勉強中ですがRailsチュートリアルでテーブルの1要素にインデックスを持たせるシーンがありました。
教材ではインデックスを持たせることで、同時にリクエストに対応し一意性を持たせることを目的としていました。
しかし、以前勉強したデータベースの教材でindexに設定することでフルスキャンと比べて早く見つけることができると書いてあったので本日はそれについての記事となります
インデックスとは何か?
インデックスを一言でいうと「データの場所を素早く探すための補助的な情報」です。書籍の巻末にある「索引(さくいん)」をイメージすると分かりやすいでしょう。書籍の索引は、キーワードとそのキーワードが掲載されているページ番号がセットでまとめられています。
データベースにおけるインデックスも同様で、あるカラム(列)の値とその値が格納されている行の場所(ポインタ)をまとめた構造になっています。これにより、知りたい値を素早く探し出すことができます。
フルテーブルスキャン
インデックスがない場合、データベースはクエリの条件に合致する行を探すためにテーブルの最初から最後まで行を1つずつチェックしていきます。これをフルテーブルスキャンと呼びます。
フルテーブルスキャンの特徴は以下の通りです
- テーブルの行数が少ないうちは気になりませんが、行数が増えるほど処理時間が長くなります。
- 条件に合致する行がわずか数行でも、最悪の場合はテーブル全件をチェックしなければなりません。
- たとえば100万行のテーブルがあった場合、最大で100万回の確認作業が必要になる可能性があります。
インデックス検索
特定のカラムにインデックスが設定されていると、データベースはフルテーブルスキャンの代わりに、インデックス構造を使って目的の値を迅速に検索します。一般的に多くのデータベースでは**B-tree(B木)**という木構造をインデックスとして使用します。
インデックス検索の特徴は以下の通りです
- B-treeの階層構造をたどりながら、該当する値(または範囲)を短いステップで特定できます。
- 100万行あっても、探索の比較回数は「対数(log n)回程度」に抑えられます。フルテーブルスキャンと比べて圧倒的に少ない回数で目的の行を見つけられます。
- 最終的にインデックスが示す行の場所を参照することで、該当の行を直接取り出せるのが大きな強みです。
なぜB-treeインデックスは速いのか?
B-treeインデックスは、データが階層的(ツリー状)に配置されており、バランスが保たれている構造です。このバランスにより、ツリーの深さが少なくなり、検索にかかるステップ数が飛躍的に減ります。
例: 100万件のデータを持つテーブル
- フルテーブルスキャン
- 100万行を1件ずつチェック → 最悪で100万回比較
- B-treeインデックス検索
- 高々20ステップ程度(2の20乗 ≈ 100万)で目的の値に到達可能
インデックスを設定するときの注意点
ストレージの増加
インデックスはテーブルとは別の領域にデータを持つため、ストレージ使用量が増加します。テーブルに対して多くのインデックスを設定すると、思わぬディスク消費を招くことがあります。
書き込みコストの増大
テーブルにINSERT・UPDATE・DELETEなどの操作を行うと、インデックスも更新が必要になります。そのため、インデックスが多いと書き込み系のパフォーマンスが低下する可能性があります。
すべてのカラムをインデックスにするわけではない
「読み取りパフォーマンスを良くするために、すべてのカラムにインデックスを張ればいいのでは?」と思うかもしれませんが、上記のようにストレージと更新コストの増大がネックになります。
よく検索に使われるカラム や 絞り込み条件に頻繁に利用されるカラムなど、用途に応じて戦略的に設定することが重要です。
まとめ
- インデックスはデータベースの検索を高速化するための強力な仕組みです。
- B-treeインデックスなどの木構造を利用して、フルテーブルスキャンに比べ圧倒的に少ない比較回数で目的の行にたどり着けます。
- ただし、インデックスは作れば作るほど書き込みコストやストレージを圧迫するため、必要なカラムに絞って戦略的に作成することがポイントです。
Railsの勉強が一通り落ち着いて、時間ができたらまたデータベースの書籍を用いて復習しようと思いました