データベースのインデックス付けはどのように機能しますか?[閉まっている]


2420

データセットのサイズが大きくなると、インデックス付けが非常に重要になるので、データベースに依存しないレベルでインデックス付けがどのように機能するかを誰かが説明できますか?

フィールドにインデックスを付けるクエリの詳細については、データベースの列にインデックスを付ける方法をご覧ください。

回答:


3547

なぜそれが必要なのですか?

データがディスクベースのストレージデバイスに保存される場合、データのブロックとして保存されます。これらのブロックは全体としてアクセスされるため、アトミックディスクアクセス操作になります。ディスクブロックは、リンクリストとほとんど同じ方法で構成されます。どちらにもデータのセクション、次のノード(またはブロック)の場所へのポインターが含まれており、両方を連続して格納する必要はありません。

いくつかのレコードは1つのフィールドでしか並べ替えることができないため、並べ替えられていないフィールドを検索するには、N/2ブロックアクセス(平均)を必要とする線形検索が必要Nです。テーブルがまたがっています。そのフィールドが非キーフィールド(つまり、一意のエントリを含まない)の場合、Nブロックアクセス時にテーブルスペース全体を検索する必要があります。

ソートされたフィールドでは、log2 Nブロックアクセスのあるバイナリ検索を使用できます。また、非キーフィールドを指定してデータがソートされるため、より高い値が見つかれば、テーブルの残りの部分で重複する値を検索する必要はありません。したがって、パフォーマンスの向上はかなりのものです。

索引付けとは何ですか?

索引付けは、複数のフィールドで多数のレコードをソートする方法です。テーブルのフィールドにインデックスを作成すると、フィールド値を保持する別のデータ構造と、関連するレコードへのポインタが作成されます。次に、このインデックス構造がソートされ、バイナリ検索を実行できるようになります。

インデックス作成の欠点は、インデックスがMyISAMエンジンを使用してテーブルに一緒に格納されるため、これらのインデックスにはディスク上に追加のスペースが必要になることです。このファイルは、同じテーブル内の多くのフィールドにインデックスが作成されている場合、基になるファイルシステムのサイズ制限にすぐに達する可能性があります。 。

どのように機能しますか?

最初に、サンプルのデータベーステーブルスキーマの概要を説明します。

フィールド名データ型ディスク上のサイズ
id(主キー)符号なしINT 4バイト
firstName Char(50)50バイト
lastName Char(50)50バイト
emailAddress Char(100)100バイト

:varcharの代わりにcharを使用して、ディスク上の正確なサイズの値を可能にしました。このサンプルデータベースには500万行が含まれており、インデックス付けされていません。いくつかのクエリのパフォーマンスが分析されます。これらは、id(ソートされたキーフィールド)を使用するクエリと、firstName(非キーのソートされていないフィールド)を使用するクエリです。

例1 -ソートされていないフィールド対ソート

バイトのr = 5,000,000レコード長を与える固定サイズのレコードのサンプルデータベースがあるR = 204とすると、それらは、デフォルトのブロックサイズB = 1,024バイトを使用しているMyISAMエンジンを使用してテーブルに格納されます。テーブルのブロック化因数は、bfr = (B/R) = 1024/204 = 5ディスクブロックごとのレコードです。テーブルを保持するために必要なブロックの総数は、blocksですN = (r/bfr) = 5000000/5 = 1,000,000

N/2 = 500,000idフィールドがキーフィールドである場合、idフィールドの線形検索では、値を見つけるためにブロックアクセスの平均が必要になります。しかし、idフィールドもソートされるため、平均的なlog2 1000000 = 19.93 = 20ブロックアクセスを必要とするバイナリ検索を実行できます。これは劇的な改善であることがすぐにわかります。

現在、firstNameフィールドはソートもキーフィールドもされていないため、バイナリ検索は不可能であり、値も一意ではありません。したがって、テーブルは、正確なN = 1,000,000ブロックアクセスのために最後まで検索する必要があります。インデックス作成が修正を目的とするのはこの状況です。

インデックスレコードにインデックス付きフィールドと元のレコードへのポインターのみが含まれている場合、それが指すマルチフィールドレコードよりも小さくなることは当然です。したがって、インデックス自体は元のテーブルより少ないディスクブロックしか必要としないため、反復処理に必要なブロックアクセスが少なくなります。firstNameフィールドのインデックスのスキーマの概要を以下に示します。

フィールド名データ型ディスク上のサイズ
firstName Char(50)50バイト
(レコードポインタ)スペシャル4バイト

:MySQLのポインターは、テーブルのサイズに応じて、長さが2、3、4、または5バイトです。

例2 -索引

r = 5,000,000インデックスレコード長がR = 54バイトで、デフォルトのブロックサイズB = 1,024バイトを使用したレコードのサンプルデータベースがあるとします。インデックスのブロック化因数は、bfr = (B/R) = 1024/54 = 18ディスクブロックごとのレコードです。インデックスを保持するために必要なブロックの総数は、blocksですN = (r/bfr) = 5000000/18 = 277,778

これで、firstNameフィールドを使用した検索でインデックスを利用してパフォーマンスを向上させることができます。これにより、log2 277778 = 18.08 = 19ブロックアクセスの平均を伴うインデックスのバイナリ検索が可能になります。実際のレコードのアドレスを見つけるには、読み取りにさらにブロックアクセスが必要であり、合計で19 + 1 = 20ブロックアクセスになるため、非インデックステーブルでfirstName一致を見つけるのに必要な1,000,000ブロックアクセスとはかけ離れています。

いつ使用すべきですか?

インデックスの作成には追加のディスク領域が必要であり(上記の例から277,778ブロック余分、最大28%増加)、インデックスが多すぎるとファイルシステムのサイズ制限に起因する問題が発生する可能性があるため、適切な選択を行うために慎重に検討する必要がありますインデックスを付けるフィールド。

インデックスは、レコード内の一致するフィールドの検索を高速化するためにのみ使用されるため、出力のみに使用されるインデックスフィールドは、挿入または削除操作を実行するときのディスクスペースと処理時間の無駄になるだけです。避けるべきです。また、バイナリ検索の性質を考えると、データのカーディナリティまたは一意性は重要です。カーディナリティが2のフィールドでインデックスを作成するとデータが半分に分割されますが、カーディナリティが1,000の場合は約1,000レコードが返されます。カーディナリティがこのように低い場合、効果は線形ソートに減少し、カーディナリティがレコード番号の30%未満の場合、クエリオプティマイザーはインデックスの使用を回避し、インデックスをスペースの無駄にします。


8
バイナリ検索は、データが一意であるときに実行できますか?最小カーディナリティが重要であるとおっしゃいましたが、アルゴリズムは単純なバイナリ検索ではありません。この近似(〜log2 n)はプロセス時間にどのように影響しますか?
シャンプー

9
@AbhishekShivkumar:すばらしい質問です。インデックステーブルには、データテーブルと同じ数の行があると思います。また、このフィールドには2つの値(true / falseのブール値)しかなく、値がtrueのレコードが必要な場合、最初のパスで結果セットを半分にすることしかできません。2番目のパスでは、すべてのレコードの値がtrueになるため、区別するための根拠はありません。今度はデータテーブルを線形に検索する必要があります。したがって、インデックス付きの列を決定する際にカーディナリティを考慮する必要があると述べました。この場合、そのような列にインデックスを付けることは意味がありません。私が正しいことを願っています:)
Saurabh Patil 2013

7
平均的な場合のブロックアクセス数はにすべきではありません(N+1)/2。考えられるすべてのケースのブロックアクセス数を合計し、それをケースの数で割ると、N*(N+1)/(2*n)どちらがになるかがわかります(N+1)/2
ajay

31
この回答には、たとえば、「インデックス付けされていないテーブルに必要な277,778ブロックアクセスとはかけ離れている」という文の中に、いくつかのタイプミスがあると思います。著者は、1,000,000ブロックのアクセスを意味しないのですか?277,778は、インデックス自体に必要なブロック数です。他にもいくつかの不正確さがあるようです:(
jcm

5
@jcm彼はそれを「インデックス付けとは」セクションで説明しました-「インデックス付けは複数のフィールドの多数のレコードをソートする方法です。テーブルのフィールドにインデックスを作成すると、フィールド値とポインタを保持する別のデータ構造が作成されますこのインデックス構造はソートされ、バイナリ検索を実行できます。」
グリンチ2014年

295

古典的な例「本の索引」

1000ページの「本」を10章で割ったものを考えてください。各セクションは100ページです。

シンプルでしょ?

ここで、「Alchemist」という単語を含む特定のチャプターを検索するとします。索引ページがないと、書籍全体または章全体をスキャンする以外に選択肢はありません。すなわち:1000ページ。

この類推は、データベースの世界では「フルテーブルスキャン」として知られています。

ここに画像の説明を入力してください

しかし、インデックスページがあれば、どこに行けばいいのかわかります。さらに、重要な特定の章を検索するには、毎回、何度もインデックスページを確認する必要があります。一致するインデックスを見つけたら、残りをスキップして、その章に効率的にジャンプできます。

しかし、実際の1000ページに加えて、インデックスを表示するためにさらに10ページが必要になるため、合計で1010ページになります。

したがって、インデックスは、効率的なルックアップのために、インデックス付き列の値とインデックス付き行へのポインタをソートされた順序で格納する個別のセクションです。

学校では物事は簡単ですよね。:P


24
本当にいいアナロジー!おかしい私は本の索引とdbの索引の間の接続をしませんでした
Yolo Voe '11

2
これは私に考えさせます、LibraryまたはGrocery Store あなたは食料品店でインデックスを持っていないとイメージできますか? Where's The Beef?!? Oh its next to the Restrooms, a mop, and makeup
JayRizzo 2018

3
「しかし、最初に索引ページがあれば、そこにいます。」「あなたがいる」とはどういう意味ですか?
Frisbetarian 2018

2
索引は通常、本の後ろにあり、目次は前にあります。しかし、列の順序は重要ではないので、これは類推をさらに良くします。
undrline

1
あなたの説明はとても簡単に取り入れられます。他の人々は物事を説明するのに洗練された用語を使う傾向があります。私は複数の賛成票を出せるといいのですが。
emeraldhieu

241

これを初めて読んだときは、とても役に立ちました。ありがとうございました。

それ以来、インデックスを作成することのマイナス面についていくつかの洞察を得ました。1つのインデックスでテーブル(UPDATEまたはINSERT)に書き込む場合、実際にはファイルシステムで2つの書き込み操作があります。1つはテーブルデータ用で、もう1つはインデックスデータ用です(そしてそれを再ソートします(クラスタ化されている場合は、テーブルデータを再ソートします))。テーブルとインデックスが同じハードディスク上にある場合、これにより多くの時間がかかります。したがって、インデックス(ヒープ)のないテーブルは、より高速な書き込み操作を可能にします。(2つのインデックスがある場合、3つの書き込み操作が発生することになります)

ただし、インデックスデータとテーブルデータ用に2つの異なるハードディスク上の2つの異なる場所を定義すると、時間のコストが増加するという問題を軽減または解消できます。これには、必要なハードディスク上の対応するファイルを含む追加のファイルグループの定義と、必要に応じたテーブル/インデックスの場所の定義が必要です。

インデックスに関するもう1つの問題は、データが挿入されるときのインデックスの断片化です。REORGANIZEそれを行うには、ルーチンを作成する必要があります。

特定のシナリオでは、インデックスのあるテーブルよりもヒープの方が便利です。

例:-競合する書き込みが多数あるが、レポート作成のために営業時間外に夜間に1回だけ読み取る場合。

また、クラスター化インデックスと非クラスター化インデックスの違いもかなり重要です。

私を助けました:- クラスタ化インデックスと非クラスタ化インデックスは実際にはどういう意味ですか?


3
これらのインデックス作成の問題は、マスターとスレーブのように2つの異なるデータベースを維持することで解決できると思います。マスターを使用してレコードを挿入または更新できる場所。索引付けなし。そして、スレーブは正しいインデックス付けで読み取るために使用できますか?
bharatesh 2014年

14
いいえ、間違っています。ごめんなさい。テーブルのコンテンツだけでなく、インデックス構造とコンテンツ(bツリー、ノード)も更新する必要があります。マスターとスレーブの概念はここでは意味がありません。ただし、2番目のデータベースに複製またはミラーリングすることで実現可能なのは、最初のデータベースからワークロードを取り除くために分析が行われることです。第二のデータベースは、データのコピーを開催すること、そのデータにインデックスを。
Der U

3
Y A...!私のコメントを読んで、正しく理解してください。同じことを言って、私はマスターとスレーブを(何であれ)「2番目のデータベースに複製またはミラーリングして、そのワークロードを最初のデータベースから取り去るために分析が行われます。2番目のデータベースはデータとインデックスのコピーを保持します。そのデータ」
バハラテッシュ2014年

6
2番目のデータベース(ミラーリングまたは複製が行われるスレーブ)では、最初のデータベースと同じようにすべてのデータ操作が行われます。dmlの操作ごとに、その2番目のデータベースのインデックスで「これらのインデックス作成の問題」が発生します。インデックスが必要であり、迅速な分析のために作成されているため、最新の状態に維持する必要がある場合は、そのメリットはわかりません。
Der U

231

インデックスは、データベース内の特定の列の検索を高速化する単なるデータ構造です。この構造は通常、Bツリーまたはハッシュテーブルですが、他の論理構造でもかまいません。


29
インデックス作成が本質的に何であるかについての簡単な説明を見つけようとしているときにこのリストを見つけたので、この回答の100万回を1倍にしました。
Josh Burson

1
「単なるデータ構造」は「データへの追加」を意味しないことに注意してください。場合によっては(たとえば、「非クラスター化インデックス」)、データのレイアウトを決定することもあります(たとえば、「クラスター化インデックス」)。
Pablo H

161

次に、クエリを実行して、「Abc」という名前の従業員の詳細をすべて検索するとします。

SELECT * FROM Employee 
WHERE Employee_Name = 'Abc'

インデックスがないとどうなりますか?

データベースソフトウェアは、文字どおりEmployeeテーブルのすべての行を調べて、その行のEmployee_Nameが 'Abc'かどうかを確認する必要があります。我々はその中に名前「ABC」ですべての行をしたいので、我々は名前「ABC」でただ一つの行を見つけたら、名前を持つ他の行があるかもしれませんのでそして、私たちは、探して停止することはできませんAbcの。したがって、最後の行までのすべての行を検索する必要があります。つまり、このシナリオでは数千の行をデータベースで調べて、「Abc」という名前の行を見つける必要があります。これはいわゆる全表スキャンです

データベースインデックスがパフォーマンスにどのように役立つか

インデックスを持つことの全体的なポイントは、調査する必要があるテーブルのレコード/行の数を本質的に削減することにより、検索クエリを高速化することです。インデックスは、テーブル内の特定の列の値を格納するデータ構造(最も一般的にはBツリー)です。

Bツリーインデックスはどのように機能しますか?

B-treeがインデックスの最も一般的なデータ構造である理由は、それらが時間効率が良いという事実によるものです-ルックアップ、削除、および挿入はすべて対数時間で実行できるためです。また、B-treeがより一般的に使用されるもう1つの主な理由は、B-tree内に格納されているデータをソートできるためです。RDBMSは通常、インデックスに実際に使用されるデータ構造を決定します。ただし、特定のRDBMSを使用する一部のシナリオでは、インデックス自体を作成するときに、データベースで使用するデータ構造を実際に指定できます。

ハッシュテーブルインデックスはどのように機能しますか?

ハッシュインデックスが使用される理由は、ハッシュテーブルは値を検索するだけの場合、非常に効率的だからです。そのため、文字列と等しいかどうかを比較するクエリは、ハッシュインデックスを使用すると非常に高速に値を取得できます。

たとえば、前に説明したクエリは、Employee_Name列に作成されたハッシュインデックスの恩恵を受けることができます。ハッシュインデックスが機能する方法は、列の値がハッシュテーブルへのキーとなり、そのキーにマップされた実際の値がテーブル内の行データへのポインターになるというものです。ハッシュテーブルは基本的に連想配列であるため、一般的なエントリは「Abc => 0x28939」のようになります。0x28939は、Abcがメモリに格納されているテーブル行への参照です。ハッシュテーブルインデックスで「Abc」などの値を検索し、メモリ内の行への参照を取得する方が、テーブルをスキャンしてEmployee_Name列の値が「Abc」であるすべての行を見つけるよりも明らかに高速です。

ハッシュインデックスの欠点

ハッシュテーブルは並べ替えられたデータ構造ではなく、ハッシュインデックスが役に立たない多くの種類のクエリがあります。たとえば、40歳未満のすべての従業員を検索するとします。ハッシュテーブルインデックスを使用してどうすればよいでしょうか。まあ、それは不可能です。ハッシュテーブルはキーと値のペアを検索する場合にのみ有効です。つまり、等価性をチェックするクエリです。

データベースインデックスの正確には何ですか? これで、データベースインデックスがテーブルの列に作成され、インデックスがその特定の列に値を格納することがわかりました。ただし、データベースインデックスは同じテーブルの他の列に値を格納しないことを理解することが重要です。たとえば、Employee_Name列にインデックスを作成する場合、これはEmployee_Age列とEmployee_Address列の値もインデックスに格納されないことを意味します。他のすべての列をインデックスに格納しただけの場合は、テーブル全体の別のコピーを作成するのと同じようになり、スペースを取りすぎて非常に非効率になります。

データベースは、インデックスを使用するタイミングをどのようにして知るのですか? 「SELECT * FROM Employee WHERE Employee_Name = 'Abc'」のようなクエリが実行されると、データベースは、クエリ対象の列にインデックスがあるかどうかを確認します。Employee_Name列にインデックスが作成されていると仮定すると、データベースは、インデックスを使用して検索対象の値を見つけることが実際に意味があるかどうかを判断する必要があります。 、テーブル全体をスキャンするだけでより効率的です。

データベースインデックスを作成するコストはどのくらいですか?

スペースを使用します。また、テーブルが大きいほど、インデックスも大きくなります。インデックスのもう1つのパフォーマンスヒットは、対応するテーブルの行を追加、削除、または更新するたびに、同じ操作をインデックスに対して実行する必要があることです。インデックスには、インデックスがカバーするテーブルの列にあるものと同じ分までのデータを含める必要があることに注意してください。

一般的なルールとして、インデックス付けされた列のデータが頻繁にクエリされる場合にのみ、インデックスをテーブルに作成する必要があります。

こちらもご覧ください

  1. 一般にどの列が適切なインデックスを作成しますか?
  2. データベースインデックスのしくみ

4
「データベースインデックスは他の列に値を格納しません」-真ではありません。
mustaccio

2
@mustaccio:インデックスには、インデックス付きの列のみを持つ行の参照が格納されます(私が知る限り)。私は間違っているかもしれません。インデックスに他の列の値が格納されているというリファレンスはありますか?
Somnath Muluk

3
@To Downvoters:私が改善できるように、何が悪いのかを説明してもらえますか?
Somnath Muluk

2
たとえば、SQL ServerのクラスタリングインデックスやDB2のCREATE INDEX ... INCLUDE句を確認します。私の見解では、あなたの回答には一般化が多すぎます。
Mustaccio

11
@mustaccio:したがって、デフォルトでcreate indexは他の列は含まれず、なぜ含まれるべきなのか。If we did just store all the other columns in the index, then it would be just like creating another copy of the entire table, which would take up way too much space and would be very inefficient.。これはインデックスのより一般化されたバージョンです。CREATE INDEX ... INCLUDE他の列を考慮した新しいバージョンです。私が説明した投稿は、より一般的なバージョンを検討しています。すべてのデータベースを考慮すると、インデックスはどのように機能するでしょうか。だよね?回答は反対票に値すると思いますか?
Somnath

97

簡単な説明!

インデックスは、テーブルの特定の列の値を格納するデータ構造にすぎません。インデックスはテーブルの列に作成されます。

例:私たちは、と呼ばれるデータベーステーブル持っているUser3つの列とを- NameAgeAddressUserテーブルに数千の行があると仮定します。

ここで、クエリを実行して、「John」という名前のユーザーのすべての詳細を検索するとします。次のクエリを実行すると、

SELECT * FROM User 
WHERE Name = 'John'

データベースソフトウェアは、文字どおりUserテーブルのすべての行を調べてName、その行のが「John」であるかどうかを確認する必要があります。これには時間がかかります。

これはindex私たちを助ける場所です:インデックスは、調査する必要があるテーブルのレコード/行の数を本質的に削減することにより、検索クエリを高速化するために使用されます。

インデックスを作成する方法:

CREATE INDEX name_index
ON User (Name)

ANは、indexから成る列の値(例:ジョン)のテーブルから、それらの値はに格納されたデータ構造

したがって、データベースでインデックスが使用され、Johnという名前の従業員が検索されます。これは、おそらくインデックスがユーザー名でアルファベット順にソートされるためです。また、並べ替えられているため、「J」で始まるすべての名前がインデックス内で互いに隣り合うため、名前の検索がはるかに高速になります。


1
インデックスは、列の並べ替え順序を意味しません
オリゴフレン

4
ありがとう。これは私の理解に役立ちました。したがって、基本的にインデックスは、ソートされた列データのレプリカです。通常、列データはデータが挿入された順序になっています。
ニール、

34

簡単な提案です。インデックス作成には追加の書き込みとストレージスペースがかかるため、アプリケーションで追加の挿入/更新操作が必要な場合は、インデックスなしのテーブルを使用することをお勧めしますが、より多くのデータ取得操作が必要な場合は、インデックス付きのテーブル。


6
これはコメントであり、回答ではありません。
RonJohn 2018

5
それは一般的な発言であるため、この方法でより見やすくなり、より役立ちます。これをコメントとして追加する必要がある回答はどれですか。
pfabri

1
おそらくOPに関するコメント
guyarad

33

データベースインデックスを本のインデックスと考えてください。

犬についての本があり、たとえばジャーマンシェパードについての情報を知りたい場合は、もちろん本のすべてのページをめくって、探しているものを見つけることができますが、これはもちろん時間がかかるもので、とても早い。

別のオプションは、本の[インデックス]セクションに移動し、探しているエンティティの名前(この例ではジャーマンシェパード)を使用して探しているものを見つけ、ページ番号を確認することです。あなたが探しているものをすぐに見つけてください。

データベースでは、ページ番号は、エンティティが配置されているディスク上のアドレスにデータベースを転送するポインタと呼ばれます。同じジャーマンシェパードの例えを使用すると、次のようなもの( "ジャーマンシェパード"、0x77129)ができ0x77129ます。ここで、はジャーマンシェパードの行データが格納されているディスク上のアドレスです。

つまり、インデックスは、クエリ検索を高速化するために、テーブルの特定の列の値を格納するデータ構造です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.