通常、主キーを選択するときは、クラスター化されたキーも選択します。 2つはよく混同されますが、違いを理解する必要があります。
主キー 論理的なビジネス 要素。主キーは、アプリケーションがエンティティを識別するために使用します。主キーに関する説明は、主に自然キーと代理キーのどちらを使用するかです。リンクについてはさらに詳しく説明しますが、基本的な考え方は、自然キーはssn
などの既存のエンティティプロパティから派生するというものです。 またはphone number
、サロゲートキーは、id
のように、ビジネスエンティティに関しては何の意味もありません。 またはrowid
通常、タイプはIDENTITY
です。 またはある種のuuid。私の個人的な意見では、サロゲートキーは自然キーよりも優れており、ローカルのみのアプリケーションのID値、あらゆる種類の分散データのGUIDを常に選択する必要があります。主キーは、エンティティの存続期間中に変更されることはありません。
クラスター化されたキー テーブル内の行の物理ストレージを定義するキーです。ほとんどの場合、それらは主キー(論理エンティティ識別子)と重複しますが、実際には強制も必須もされていません。 2つが異なる場合は、主キーを実装する非クラスター化一意インデックスがテーブルにあることを意味します。クラスター化されたキー値は、行の存続期間中に実際に変更される可能性があり、その結果、行はテーブル内で新しい場所に物理的に移動されます。主キーをクラスター化キーから分離する必要がある場合(場合によっては分離する必要があります)、適切なクラスター化キーを選択することは、主キーを選択するよりもはるかに困難です。クラスタ化されたキーの設計を推進する主な要因は2つあります。
- 一般的なデータアクセスパターン 。
- ストレージに関する考慮事項 。
データアクセスパターン 。これにより、テーブルのクエリと更新の方法がわかります。クラスタ化されたキーによって、テーブル内の行の実際の順序が決まることに注意してください。特定のアクセスパターンでは、一部のレイアウトは、クエリ速度や更新の同時性に関して世界ですべての違いをもたらします:
-
現在のデータとアーカイブデータ。多くのアプリケーションでは、当月のデータに頻繁にアクセスしますが、過去のデータにはほとんどアクセスしません。このような場合、テーブルの設計では、トランザクション日付によるテーブルのパーティション分割が使用されます。多くの場合、スライディングウィンドウアルゴリズムが使用されます。今月のパーティションはホットファストディスクにあるファイルグループに保持され、アーカイブされた古いデータは、より安価で低速のストレージでホストされているファイルグループに移動されます。明らかに、この場合、クラスター化されたキー(日付)は主キー(トランザクションID)ではありません。クエリオプティマイザは、クエリが現在のパーティションにのみ関心があり、過去のパーティションを見ることさえできないことを検出できるため、2つの分離はスケール要件によって決まります。
-
FIFOキュースタイルの処理。この場合、テーブルには2つのホットスポットがあります。挿入が発生するテール(エンキュー)と削除が発生するヘッド(デキュー)です。クラスタ化されたキーは、これを考慮に入れて、ディスク上のテールとヘッドの位置を物理的に分離するようにテーブルを編成する必要があります。これにより、たとえば、エンキューとデキューの間の同時実行が可能になります。エンキューオーダーキーを使用します。 純粋 テーブルに主キーがないため、このクラスター化されたキーが唯一のキーです(メッセージが含まれています)。 、エンティティではありません )。ただし、ほとんどの場合、キューは純粋ではなく、エンティティのストレージとしても機能し、キュー間の線としても機能します。 およびテーブル ぼやけています。この場合、クラスター化されたキーにはできない主キーもあります。エンティティは再エンキューされる可能性があるため、エンキュー順序のクラスター化されたキーの値は変更されますが、主キーの値は変更できません。分離が見られないことが、ユーザーテーブルでバックアップされたキューを正しく処理するのが非常に難しく、デッドロックが発生する主な理由です。エンキューとデキューが発生するため、キューの末尾と先頭にローカライズされるのではなく、テーブルをインターリーブされます。
-
相関処理。アプリケーションが適切に設計されている場合、関連するアイテムの処理をワーカースレッド間で分割します。たとえば、プロセッサは8つのワーカースレッド(サーバー上の8つのCPUと一致するように)を持つように設計されているため、プロセッサはデータを相互に分割します。ワーカー1は、AからE、ワーカー2 FからJなどの名前のアカウントのみを取得します。このような場合、テーブルは実際にはアカウント名(または、アカウント名の最初の文字の左端にある複合キー)でクラスター化する必要があります。ワーカーがクエリと更新をテーブルにローカライズできるようにします。このようなテーブルには、各作業者が現在集中しているエリアの周囲に8つの異なるホットスポットがありますが、重要なことは、それらが重ならない(ブロッキングがない)ことです。この種の設計は、ハイスループットOLTP設計およびTPCCベンチマークロードで一般的です。この種のパーティショニングは、バッファプール(NUMAローカリティ)にロードされたページのメモリ位置にも反映されますが、私は逸脱します。
ストレージに関する考慮事項 。クラスター化されたキー幅 テーブルのストレージに大きな反響があります。 1つは、キーがBツリーのすべての非リーフページのスペースを占めるため、大きなキーはより多くのスペースを占めることになります。次に、多くの場合、より重要なのは、クラスター化されたキーが、クラスター化されていないすべてのキーによってルックアップキーとして使用されるため、すべて 非クラスター化キーは、各行のクラスター化キーの全幅を格納する必要があります。これが、varchar(256)のような大きなクラスター化キーを作成し、クラスター化インデックスキーの選択を不適切にします。
また、キーの選択はクラスター化インデックスの断片化に影響を与え、パフォーマンスに大幅な影響を与える場合があります。
これらの2つの力は相反する場合があり、データアクセスパターンには特定の大きなクラスター化されたキーが必要であり、ストレージの問題が発生します。そのような場合はもちろんバランスが必要ですが、魔法の公式はありません。あなたは測定し、スイートスポットに到達するためにテストします。
では、これらすべてから何を作るのでしょうか? 常に、entity_id IDENTITY(1,1) NOT NULL
の形式の主キーでもあるクラスター化されたキーを検討することから始めます。 。必要に応じて、2つを分離し、それに応じてテーブルを整理します(たとえば、日付によるパーティション化)。