web-dev-qa-db-ja.com

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

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

「データベースにはすでに「PK__tablenameprefix__179E2ED8F259C33B」という名前のオブジェクトがあります。」

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

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

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

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

14
jl6

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だけオフセットされた符号付き整数カウンターを表し、負の値はすべて最大の符号付き整数にラップされます。 (16000057の重要性は、これが 連続して作成されるobject_id の間に適用される増分)であることです。これにより、一意性が保証されます。

2012以降では、制約のobject_idと名前の最後の8文字を符号付きintの16進数表現として扱うことで得られる整数の間にパターンがまったくありません。

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

enter link description here

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

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制約またはインデックスを作成できませんでした。以前のエラーを参照してください。

16
Martin Smith

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

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

Nテーブルの場合、N *(N-1)/ 2のペアがあるため、ここでは約1016 ペア。衝突の確率が2の場合-64、単一のペアが衝突しない確率は1-2です。-64、しかし非常に多くのペアがあるため、ここで衝突がない確率は約(1-2-641016、または1/10,000以上。例を参照してください https://preshing.com/20110504/hash-collision-probabilities/

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