サロゲートvs自然キー:パフォーマンスの違いに関するハードナンバー?
-
22-07-2019 - |
質問
代理キーと自然キーの間には健全な議論があります:
私は、自然のキーが完全に明白であり、変更しないことが保証されていない限り、サロゲートキーを使用する必要があると考えています。次に、自然キーに一意性を適用する必要があります。これは、ほぼ常に代理キーを意味します。
Companyテーブルから始まる2つのアプローチの例:
1:サロゲートキー:テーブルには、PK(およびID)であるIDフィールドがあります。会社名は州ごとに一意である必要があるため、一意の制約があります。
2:自然キー:テーブルはCompanyNameとStateをPKとして使用し、PKと一意性の両方を満たします。
他の10個のテーブルでCompany PKが使用されているとします。私の仮説は、それを裏付ける数字はありませんが、ここではサロゲートキーアプローチがはるかに高速になるということです。
自然キーについて私が見た唯一の説得力のある議論は、2つの外部キーを自然キーとして使用する多対多のテーブルに関するものです。その場合は理にかなっていると思います。ただし、リファクタリングが必要な場合は問題が発生する可能性があります。それはこの投稿の範囲外です。
サロゲートキー vs。を使用する一連のテーブルでパフォーマンスの違いを比較する記事を誰もが見ましたか? 自然キー? SOとGoogleを調べても、価値のあるものは何も得られず、多くの理論が作成されました。
重要な更新:この質問に答えるテストテーブルのセットの構築を開始しました。次のようになります:
- PartNatural-使用するパーツテーブル PKとしての一意のPartNumber
- PartSurrogate-パーツテーブル ID(int、identity)をPKとして使用し、 PartNumberに一意のインデックスがあります
- プラント-PKとしてのID(int、identity)
- エンジニア-PKとしてのID(int、identity)
すべての部品はプラントに結合され、プラントの部品のすべてのインスタンスはエンジニアに結合されます。このテストベッドに問題がある場合は、今がその時です。
解決
両方を使用してください! Natural Keysは、データベースの破損を防ぎます(一貫性のないほうがいいかもしれません)。 「正しい」とき自然キー、(重複行を排除するため)は、パフォーマンスの目的のために、長さ、または関連する列の数のためにパフォーマンスが低下します。サロゲートキーを追加して、自然キーの代わりに他のテーブルの外部キーとして使用することもできます。 。ただし、自然なキーは、データの破損を防ぎ、データベースの一貫性を確保するために、代替キーまたは一意のインデックスのままにしてください...
フーアの大部分(この問題の「討論」)の多くは、 プライマリキー を使用する必要があるという誤った仮定に起因する可能性があります。 >他のテーブルの結合および外部キー。これは間違っています。任意の key を他のテーブルの外部キーのターゲットとして使用できます。主キー、代替キー、または一意のインデックスまたは一意の制約を指定できます。また、結合に関しては、結合条件に何でも使用できます。キー、IDEX、または一意である必要さえありません!! (ただし、一意でない場合は、作成するデカルト積に複数の行が含まれます。)
他のヒント
自然キーは、タイプではなく値がサロゲートキーと異なります。
システムで生成された slug
の VARCHAR
など、任意のタイプを代理キーに使用できます。
ただし、サロゲートキーに最もよく使用されるタイプは、 INTEGER
および RAW(16)
(または RDBMS
が GUID
's)、
サロゲート整数と自然整数( SSN
など)の比較には、まったく同じ時間がかかります。
比較する VARCHAR
は照合を考慮に入れますが、通常は整数よりも長いため、効率が低下します。
2つの INTEGER
のセットを比較することは、単一の INTEGER
を比較するよりもおそらく効率的ではありません。
サイズの小さいデータ型では、この差はおそらくページのフェッチ、インデックスのトラバース、データベースラッチの停止などに必要な時間のパーセントです。
そして、ここに数字があります( MySQL
内):
CREATE TABLE aint (id INT NOT NULL PRIMARY KEY, value VARCHAR(100));
CREATE TABLE adouble (id1 INT NOT NULL, id2 INT NOT NULL, value VARCHAR(100), PRIMARY KEY (id1, id2));
CREATE TABLE bint (id INT NOT NULL PRIMARY KEY, aid INT NOT NULL);
CREATE TABLE bdouble (id INT NOT NULL PRIMARY KEY, aid1 INT NOT NULL, aid2 INT NOT NULL);
INSERT
INTO aint
SELECT id, RPAD('', FLOOR(RAND(20090804) * 100), '*')
FROM t_source;
INSERT
INTO bint
SELECT id, id
FROM aint;
INSERT
INTO adouble
SELECT id, id, value
FROM aint;
INSERT
INTO bdouble
SELECT id, id, id
FROM aint;
SELECT SUM(LENGTH(value))
FROM bint b
JOIN aint a
ON a.id = b.aid;
SELECT SUM(LENGTH(value))
FROM bdouble b
JOIN adouble a
ON (a.id1, a.id2) = (b.aid1, b.aid2);
t_source
は、 1,000,000
行の単なるダミーテーブルです。
aint
および adouble
、 bint
および bdouble
には、 aint
は PRIMARY KEY
として整数を持ち、 adouble
は2つの同一の整数のペアを持ちます。
私のマシンでは、両方のクエリが14.5秒、+ /-0.1秒実行されます
パフォーマンスの差がある場合、それは変動範囲内です。