私はデータベースの大部分がBツリーを使用していることを知っており、バランスの取れたバイナリツリーを使用すると、IDやその他の主キーによる順序付けでソート時間をどのように高速化できるかを確認できます。しかし、データベースはどのようにNameやAgeなどのさまざまなフィールドをORDER_BYできますか?それは、データに対してマージソートやクイックソートなどの効率的なソートアルゴリズムを実行するだけですか?ストレージ)。 IDの順序と名前の順序は、Bツリーに並べ替えられたすべてのフィールドを格納しない限り異なるため、他の並べ替えアルゴリズムを実行する必要があります。
TLDR:Bツリーに格納されたデータが主キーに基づいている場合、データベースはどのように非主キーフィールドで高速ソートを実行できますか。
テーブル内のデータは順序付けできます(これはクラスター化インデックスと呼ばれます)が、並べ替え順序は1つしかありません。それ以上のインデックスはデータを並べ替えませんが、データを含まず、行の順序のみを含みます。データベースに目的のORDER BY句のインデックスがない場合は、その場で作成する必要があります。
典型的なRDBMSでは、行データはヒープと呼ばれる別のストレージ領域にあります。 Bツリーは、ヒープを指すインデックスにすぎません。任意のキーで同じヒープを指す複数のインデックスを作成するのは簡単です(フィールドを組み合わせたり、フィールドに関数を適用してキーを生成することもできます)。行データがクラスター化インデックスと呼ばれるBツリー内に格納される特別なケースがありますが、そこに2番目のBツリーがプライマリインデックスを指すようにすることもできます。
行にすばやくアクセスするために、複数の戦略が使用されます。一部の列はクエリ内で非常に高速なインデックス内に保持できます。他の列では、ページキャッシュを使用して、ヒープの最も一般的にアクセスされる部分をRAMに保持できます。十分なRAMでワーキングセットを完全にメモリに保持できる場合、データベースはインデックスがなくても十分に機能します。より大きなワーキングセットの場合、低レイテンシーシークのSSDはパフォーマンスに大きく役立ちます。
データベースが抱える課題は、クラッシュが発生した場合でも、これらの構造をすべて同期させることです。典型的なアプローチは、先読みログ(WAL)を使用することです。最初に書き込みがログに追加され、メモリキャッシュにより、古いBツリーではなくログからの読み取りが確実に処理されます。ログがディスクにフラッシュされた後、Bツリーも更新されます。 Bツリーの更新が完了する前にデータベースがクラッシュした場合、次回の起動時にWALから回復して更新を終了します。
詳細については、 このスライドデッキ のメモを参照してください。
anyデータベースがこのように機能するかどうかは、ここでは誰にもわかりませんが、ほとんどの場合、これは非常に簡単です。
indexが事前に作成されているフィールドまたはフィールドの組み合わせの場合、ORDER_BY句に関連フィールドが含まれている場合は常にこのインデックスが使用されます。そのインデックスはBツリーとして実装できます。
その他のフィールド(またはフィールドの組み合わせ)の場合、並べ替えアルゴリズムが選択され、並べ替えはその場で行われます。どのソートアルゴリズムが使用されるかは、おそらく特定のDBMSとその高度な実装に依存しますが、ソートするレコードの数、キーフィールドのサイズなどのヒューリスティックに応じてアルゴリズムを決定するシステムを想像できます。彼らはメインメモリに収まるかどうか。
通常、主キーのインデックス(Bツリーなど)を自動的に作成し、データベース管理者が他の列、列の組み合わせ、または列の値に対する式の追加のインデックスを作成できるようにします。あなたが指摘するように、これは検索速度と一部のスペース(および挿入速度)を交換します。したがって、最高のパフォーマンスを得るには、十分なインデックスを追加しますが、必要な数を超えないようにしてください。インデックスが存在しない場合、データベースは並べ替えアルゴリズムを使用して、その場でデータを並べ替えます。
通常、データベースは行データをインデックスとは別に保持します。
行はページに格納され(例: PostgreSQLでの実行方法 の詳細を参照)、各インデックスフィールド(またはフィールドの組み合わせ)には、実際の行を含まないBツリーインデックスがありますデータには、ページへのポインタとページ内のインデックスのみが含まれます。実際には、技術レベルの主キーについて特別なことは何もありません。それは単なる別のインデックスです。
この設計では、インデックス付きフィールドに関連する操作(WHERE句、ORDER BY、結合など)のみがある場合は、結果を出力する必要があるまで実際の行を調べる必要はありません。 (ページ、インデックス)タプルは行を一意に識別し、それのみを使用してすべての操作を実行でき、最後にすべての行データを一度にフェッチします。