SQL Serverは、システムで生成された制約名に競合を作成できますか?


14

SQL Server 2008データベース(非クラスター化)に数百万のテーブルを作成するアプリケーションがあります。SQL Server 2014(クラスター化)にアップグレードしようとしていますが、負荷がかかっているときにエラーメッセージが表示されます。

「データベースに「PK__tablenameprefix__179E2ED8F259C33B」という名前のオブジェクトが既に存在します」

これは、システムで生成された制約名です。ランダムに生成された64ビットの数字のように見えます。多数のテーブルが原因で衝突が発生している可能性はありますか?テーブルが1億個あると仮定すると、次のテーブルを追加するときに衝突が発生する可能性は1兆分の1未満ですが、それは均一な分布を前提としています。衝突の可能性を高めるために、SQL Serverがバージョン2008と2014の間で名前生成アルゴリズムを変更した可能性はありますか?

もう1つの重要な違いは、2014年のインスタンスがクラスター化されたペアであることですが、上記のエラーが発生する理由の仮説を立てるのに苦労しています。

PSはい、何百万ものテーブルを作成するのは非常識です。これは、私が制御できないブラックボックスのサードパーティコードです。狂気にもかかわらず、バージョン2008では機能しましたが、現在はバージョン2014では機能しません。

編集:詳細な検査では、生成されたサフィックスは常に179E2ED8で始まるようです-つまり、ランダムな部分は実際には32ビットの数字であり、衝突の確率は新しいテーブルが追加されるたびに50分の1です私が見ているエラー率にはるかに近い一致です!


テーブル名は異なりますが、少なくとも最初の11文字が同じになる命名規則を使用します。これは、SQL Serverが制約名の生成に使用するすべてのようです。
jl6

基礎となるハードウェアは異なりますが(DL380の新世代)、パフォーマンスはそれほど高くありません。この演習の目的は、スループットの向上ではなく、サポートが終了したSQL Server 2008を置き換えることであり、ハードウェアはそれに応じてプロビジョニングされています。
jl6

回答:


15

SQL Serverは、システムで生成された制約名に競合を作成できますか?

これは、制約の種類とSQL Serverのバージョンによって異なります。

CREATE TABLE T1
(
A INT PRIMARY KEY CHECK (A > 0),
B INT DEFAULT -1 REFERENCES T1,
C INT UNIQUE,
CHECK (C > A)
)

SELECT name, 
       object_id, 
       CAST(object_id AS binary(4)) as object_id_hex,
       CAST(CASE WHEN object_id >= 16000057  THEN object_id -16000057 ELSE object_id +2131483591 END AS BINARY(4)) AS object_id_offset_hex
FROM sys.objects
WHERE parent_object_id = OBJECT_ID('T1')
ORDER BY name;

drop table T1

例の結果2008

+--------------------------+-----------+---------------+----------------------+
|           name           | object_id | object_id_hex | object_id_offset_hex |
+--------------------------+-----------+---------------+----------------------+
| CK__T1__1D498357         | 491357015 | 0x1D498357    | 0x1C555F1E           |
| CK__T1__A__1A6D16AC      | 443356844 | 0x1A6D16AC    | 0x1978F273           |
| DF__T1__B__1B613AE5      | 459356901 | 0x1B613AE5    | 0x1A6D16AC           |
| FK__T1__B__1C555F1E      | 475356958 | 0x1C555F1E    | 0x1B613AE5           |
| PK__T1__3BD019AE15A8618F | 379356616 | 0x169C85C8    | 0x15A8618F           |
| UQ__T1__3BD019A91884CE3A | 427356787 | 0x1978F273    | 0x1884CE3A           |
+--------------------------+-----------+---------------+----------------------+

結果の例2017

+--------------------------+------------+---------------+----------------------+
|           name           | object_id  | object_id_hex | object_id_offset_hex |
+--------------------------+------------+---------------+----------------------+
| CK__T1__59FA5E80         | 1509580416 | 0x59FA5E80    | 0x59063A47           |
| CK__T1__A__571DF1D5      | 1461580245 | 0x571DF1D5    | 0x5629CD9C           |
| DF__T1__B__5812160E      | 1477580302 | 0x5812160E    | 0x571DF1D5           |
| FK__T1__B__59063A47      | 1493580359 | 0x59063A47    | 0x5812160E           |
| PK__T1__3BD019AE0A4A6932 | 1429580131 | 0x5535A963    | 0x5441852A           |
| UQ__T1__3BD019A981F522E0 | 1445580188 | 0x5629CD9C    | 0x5535A963           |
+--------------------------+------------+---------------+----------------------+

デフォルトの制約、チェック制約、および外部キー制約の場合、自動生成された名前の最後の4バイトは、制約のオブジェクトIDの16進バージョンです。objectid独自の保証されている名前は、一意である必要があります。Sybaseでもこれらの使用tabname_colname_objectid

ユニーク制約とプライマリキー制約の場合、Sybaseは使用します

tabname_colname_tabindid。tabindidは、テーブルIDとインデックスIDの文字列連結です。

これも一意性を保証します。

SQL Serverはこのスキームを使用しません。

SQL Server 2008と2017の両方で、システムが生成した名前の最後に8バイトの文字列を使用しますが、その最後の4バイトの生成方法に関してアルゴリズムが変更されました。

2008年の最後の4バイトは、符号付き整数カウンターを表し、負の値が最大の符号付き整数にラップobject_idされること-16000057で、by からオフセットされます。(の重要性16000057は、これが連続して作成される間に適用される増分object_idであることです)。これはまだ一意性を保証します。

2012年以降、制約のobject_idと、名前の最後の8文字を符号付きintの16進表現として処理して得られた整数の間にパターンがまったく表示されません。

2017年の呼び出しスタック内の関数名は、名前生成プロセスの一部としてGUIDを作成することを示しています(2008年には、私は言及していませんMDConstraintNameGenerator)。これはランダム性のソースを提供するためだと思います。ただし、4バイトのGUIDから16バイト全体を使用しているのではなく、制約間で変更していることは明らかです。

ここにリンクの説明を入力してください

あなたのような極端な場合に衝突の可能性がいくらか増加することを犠牲にして、新しいアルゴリズムは何らかの効率的な理由で行われたと思います。

PKのテーブル名のプレフィックスと列名(最後の8文字に先行する8文字に影響するため)が数万のテーブルで同一になる可能性がありますが、かなり再現できるため、これは非常に病理学的なケースです以下で簡単に。

CREATE OR ALTER PROC #P
AS
    SET NOCOUNT ON;

    DECLARE @I INT = 0;


    WHILE 1 = 1
      BEGIN
          EXEC ('CREATE TABLE abcdefghijklmnopqrstuvwxyz' + @I + '(C INT PRIMARY KEY)');
          SET @I +=1;
      END 

GO

EXEC #P

新しく作成されたデータベースに対してSQL Server 2017で実行された例は、1分以上で失敗しました(50,931テーブルが作成された後)

メッセージ2714、レベル16、状態30、行15データベースには既に「PK__abcdefgh__3BD019A8175067CE」という名前のオブジェクトがあります。メッセージ1750、レベル16、状態1、行15制約またはインデックスを作成できませんでした。以前のエラーを参照してください。


11

テーブルが1億個あると仮定すると、1兆分の1未満の衝突の可能性を計算します

これが「誕生日の問題」であることを忘れないでください」であることを。与えられた単一のハッシュに対して衝突を生成しようとするのではなく、多くの値のペアのどれも衝突しない確率を測定します。

したがって、N個のテーブルでは、N *(N-1)/ 2個のペアがあるため、ここでは約10 16個のペアです。衝突の確率が2 -64の場合、単一のペアが衝突しない確率は1-2 -64ですが、非常に多くのペアがある場合、ここで衝突がない確率は約(1-2 -6410 16です。、または1 / 10,000など。例:https : //preshing.com/20110504/hash-collision-probabilities/

そして、それが32ビットのハッシュである場合、衝突の確率は77kの値で1/2を超えます。


2
そして、衝突に遭遇することなくそもそも77Kの値に到達することは、それ以前のすべての以前の作成に対して幸運だった必要があるので、かなりありそうにないでしょう。衝突の累積確率が50%に達するのはどこなのか疑問に思う
Martin Smith
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.