背景
レコード管理システムのデータベースを設計しています。 INT
またはUNIQUEIDENTIFIER
を主キーとして使用することを検討しているので、これは初期の段階です。
UNIQUEIDENTIFIER
を検討する理由は、レコードを他のシステム(外部データベース)に移動できる機能が必要になる可能性が高く、GUIDとのすべての関係を処理することで、非常に簡単になるためです。 。
いくつかの調査を行った後、それぞれの長所と短所について多くの議論があり、私が関心を持っている主なものはパフォーマンスです。私の調査の結果、PKとしてUNIQUEIDENTIFIER
を使用しても、テーブルのクラスター化インデックスとして使用されていない限り、問題にはなりません。
わかりましたので、明白な解決策はそれらを2つの別々の列に分割することです:
TableA
ID: INT, Identity, Clustered Index
PK: UNIQUEIDENTIFIER, Non-clustered Index
質問
ここでの懸念は、関係を定義するときに、これが結合のパフォーマンスにどのように影響するかです。たとえば、次の「子」テーブルを見てください。
TableB
ID: INT, Identity, Clustered Index
PK: UNIQUEIDENTIFIER, Non-clustered Index
FK: UNIQUEIDENTIFIER, Non-clustered Index
このアプローチを使用することによるパフォーマンスの低下、特にテーブルの結合について心配する必要がありますか?
たとえば、次のようなクエリを使用します。
SELECT * FROM TableB JOIN TableA ON TableA.PK = TableB.FK
基本的に、これはGUIDベースのPKをサポートするための最良の設計ですか、それともパフォーマンスの低下につながりますか?私が提案したアプローチを使用することには重大な欠点がありますか?
これは、GUIDをクラスター化された、およびクラスター化されていない主キーとして使用する数年に基づいて私が見たものです。正しい答えはここにはありません。重要なのは、データにアクセスするために使用するアクセスメソッドと、返されるデータの量です。
はい、ただし、アクセス方法でGUIDをクエリの述語として使用している場合、管理する必要があるGUIDクラスタ化インデックスで問題が発生します。 、そのテーブルから複数の列を返す場合、パフォーマンスを向上させてデータを読み戻すことによる影響を軽減するために、断片化の影響を受けた方がよい場合があります。
以下は、クラスター化インデックスの場合にデータを取り出すために必要な作業が少なく、データを取得するために使用される述語が非常に基本的な例です。
/* NonClustered PK */
CREATE TABLE #T1
(
C1 INT NOT NULL ,
C2 UNIQUEIDENTIFIER NOT NULL ,
C3 VARCHAR(100) NULL ,
C4 VARCHAR(20) NULL ,
CONSTRAINT PK_T1 PRIMARY KEY NONCLUSTERED ( C2 )
);
CREATE CLUSTERED INDEX T1_C1 ON #T1 (C1);
/* Clustered PK */
CREATE TABLE #T2
(
C1 INT NOT NULL ,
C2 UNIQUEIDENTIFIER NOT NULL ,
C3 VARCHAR(100) NULL ,
C4 VARCHAR(20) NULL ,
CONSTRAINT PK_T2 PRIMARY KEY CLUSTERED ( C2 )
);
/* Insert 10 rows into each table */
INSERT INTO #T1
( C1, C2, C3, C4 )
VALUES ( 0, '58BBB460-1AFA-4177-BA78-798DA19E0C97', 'some text', 'C4 text')
, ( 1, '17E8163B-BE21-44C7-A7B7-4997265A139D', 'some text', 'C4 text')
, ( 2, '16AACAB8-CD77-4A8D-BE87-9E433CD157EC', 'some text', 'C4 text')
, ( 3, '787D0714-F92A-4963-89E5-3F5DBF518EA7', 'some text', 'C4 text')
, ( 4, '5C720476-D4BE-4047-9F73-DBB1B6B75208', 'some text', 'C4 text')
, ( 5, 'D70F81C5-8AFF-4ABE-BA64-8F5C1A1C6A90', 'some text', 'C4 text')
, ( 6, '1473E5DC-6F3E-4164-988C-E36EE7C695BE', 'some text', 'C4 text')
, ( 7, '648AEA46-4B45-41F9-AA9B-7129062391B4', 'some text', 'C4 text')
, ( 8, '49497ECB-774D-482D-8230-218E97FB2EB4', 'some text', 'C4 text')
, ( 9, 'B90504FA-CEBA-4383-A61F-82F33DAB7A0E', 'some text', 'C4 text');
INSERT INTO #T2
( C1, C2, C3, C4 )
SELECT C1 ,
C2 ,
C3 ,
C4 FROM #T1;
/* Index seek on T1 - no lookup as we include the clustering key */
SELECT C1, C2 FROM #T1 WHERE C2 = '648AEA46-4B45-41F9-AA9B-7129062391B4'
/* Index seek AND key lookup on T1 because there are columns not contained in the PK */
SELECT C1, C2, C3 FROM #T1 WHERE C2 = '648AEA46-4B45-41F9-AA9B-7129062391B4'
/* Clustered index seek on T2 as the predicate is the PK and clustered index */
SELECT C1, C2 FROM #T2 WHERE C2 = '648AEA46-4B45-41F9-AA9B-7129062391B4'
/* Stil a clustered index seek on T2 as the predicate is the PK and clustered index */
SELECT C1, C2, C3 FROM #T2 WHERE C2 = '648AEA46-4B45-41F9-AA9B-7129062391B4'
あなたが考慮に入れ、考える必要があることは次のようになります:
悲しいことに、これに対する単一の正解はありません。適切な期待を得ることができるだけであり、広範囲のテストを実行して、それらの期待が設計したどのデータモデルでも満たされるかどうかを確認し、満たされない場合は、調整してそれらを改善します。
私が提案したアプローチを使用することに重大な欠点はありますか?
言うのはとても難しいです。唯一の本当の欠点は、ほとんどのクエリが非クラスター化PKを介して行われ、非常に読み取りが重い場合、IOが大幅に増加するため、呼び出しごとのキー検索。
ストレージがそれに耐えることができれば、それは素晴らしいことです。そして、良い面としては、時間の経過とともにインデックスのメンテナンスがひどくなる断片化の問題がないことです(AGを実行していて、メンテナンス期間がない場合は、これらを再構築できないことを意味します)。
クラスタリングキーの選択は1つです。主キーの選択は別のものです。 SQL Serverの既定の動作であるため、PKで自動的にクラスター化しないでください。
INT
CIXを持つテーブルのINT
のインデックスは、レコードごとに8バイトになります。 UNIQUEIDENTIFIER
CIXを含むテーブルの場合も同様に、レコードあたり20バイトになります。NEWSEQUENTIALID()
)。これがいかに重要であるかについて、相反する報告を見てきました。 Googleの「ホットスポット」と「GUID主キー」には、さまざまな意見があります。 SSDの場合、シリアルデータは以前ほど重要ではありませんが、データが断片化されているため、追加のページルックアップを行うには依然としてコストがかかります。INT
がある場合は、一意ではなくクラスタリングインデックスに含めることもできます。Orders
をDateOrdered
でクラスタ化して、内部分析とレポートのために月別の売上のクエリをサポートするか、またはCustomerID
でクラスタ化して、すべての注文のクエリをサポートします。カスタマーポータルまたはサポートデスクの顧客。データについて詳しく知らなければ、具体的な推奨を行うことは困難です。 16バイトでは、UNIQUEIDENTIFIER
は確かに_IDENTITY INT
_より広いですが、2番目のキーを維持するためのコスト(ストレージだけでなく、脳のスペース)も現実的です。テーブルに何億ものレコードがない場合を除き、私は最も単純なソリューションから始めます。単一のUNIQUEIDENTIFIER
フィールド上のクラスター化されたPKで、NEWSEQUENTIALID()
が入力されています。