SQL Server può creare collisioni nei nomi dei vincoli generati dal sistema?


14

Ho un'applicazione che crea milioni di tabelle in un database SQL Server 2008 (non cluster). Sto cercando di aggiornare a SQL Server 2014 (cluster), ma sto colpendo un messaggio di errore quando sotto carico:

"Esiste già un oggetto denominato" PK__tablenameprefix__179E2ED8F259C33B "nel database"

Questo è un nome di vincolo generato dal sistema. Sembra un numero a 64 bit generato casualmente. È possibile che io stia vedendo delle collisioni a causa dell'elevato numero di tabelle? Supponendo di avere 100 milioni di tabelle, calcolo meno di 1 su 1 trilione di probabilità di una collisione quando aggiungo la tabella successiva, ma ciò presuppone una distribuzione uniforme. È possibile che SQL Server abbia modificato l'algoritmo di generazione dei nomi tra la versione 2008 e 2014 per aumentare le probabilità di collisione?

L'altra differenza significativa è che la mia istanza del 2014 è una coppia di cluster, ma sto lottando per formare un'ipotesi sul perché ciò genererebbe l'errore sopra riportato.

PS Sì, lo so che la creazione di milioni di tabelle è folle. Questo è un codice di terze parti in black box su cui non ho alcun controllo. Nonostante la follia, ha funzionato nella versione 2008 e ora non nella versione 2014.

Modifica: a un esame più attento, il suffisso generato sembra sempre iniziare con 179E2ED8 - il che significa che la parte casuale è in realtà solo un numero a 32 bit e le probabilità di collisioni sono solo 1 su 50 ogni volta che viene aggiunta una nuova tabella, che è una corrispondenza molto più vicina al tasso di errore che vedo!


I nomi delle tabelle sono diversi, ma usano una convenzione di denominazione che comporta che i primi 11 caratteri siano gli stessi e che sembra essere tutto ciò che SQL Server utilizza per generare il nome del vincolo.
jl6,

L'hardware sottostante è diverso (nuova generazione di DL380) ma non prestazioni significativamente superiori. Lo scopo dell'esercizio è sostituire SQL Server 2008 fuori supporto, non migliorare la velocità effettiva e l'hardware è stato fornito di conseguenza.
jl6,

Risposte:


15

SQL Server può creare collisioni nei nomi dei vincoli generati dal sistema?

Ciò dipende dal tipo di vincolo e dalla versione di 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

Risultati di esempio 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           |
+--------------------------+-----------+---------------+----------------------+

Risultati di esempio 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           |
+--------------------------+------------+---------------+----------------------+

Per i vincoli predefiniti, controllare i vincoli e i vincoli di chiave esterna, gli ultimi 4 byte del nome generato automaticamente sono una versione esadecimale dell'oggetto objectid del vincolo. Poiché objectidsono garantiti univoci, anche il nome deve essere unico. Anche in Sybase questi usanotabname_colname_objectid

Per vincoli univoci e vincoli di chiave primaria utilizzati da Sybase

tabname_colname_tabindid, dove tabindid è una concatenazione di stringhe dell'ID tabella e dell'ID indice

Anche questo garantirebbe l'unicità.

SQL Server non utilizza questo schema.

In SQL Server 2008 e 2017 utilizza una stringa di 8 byte alla fine del nome generato dal sistema, tuttavia l'algoritmo è cambiato in base alla modalità di generazione degli ultimi 4 byte.

Nel 2008 gli ultimi 4 byte rappresentano un contatore intero con segno compensata dal object_idda -16000057qualsiasi involucro valore negativo intorno a max int firmata. (Il significato di 16000057è che questo è l'incremento applicato tra successivamente creatoobject_id ). Ciò garantisce ancora unicità.

A partire dal 2012 non vedo alcun pattern tra object_id del vincolo e l'intero ottenuto trattando gli ultimi 8 caratteri del nome come la rappresentazione esadecimale di un int firmato.

I nomi delle funzioni nello stack di chiamate nel 2017 mostrano che ora crea un GUID come parte del processo di generazione del nome (Nel 2008 non vedo alcuna menzione MDConstraintNameGenerator). Immagino che ciò fornisca una fonte di casualità. Chiaramente non sta usando tutti i 16 byte del GUID in quei 4 byte che cambiano tra i vincoli.

inserisci qui la descrizione del link

Presumo che il nuovo algoritmo sia stato fatto per qualche motivo di efficienza a spese di una maggiore possibilità di collisioni in casi estremi come il tuo.

Questo è un caso abbastanza patologico in quanto richiede che il prefisso del nome della tabella e il nome della colonna del PK (nella misura in cui ciò influisce sugli 8 caratteri che precedono l'8 finale) siano identici per decine di migliaia di tabelle prima che diventi probabile ma possano essere riprodotti abbastanza facilmente con il seguito.

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

Un esempio eseguito su SQL Server 2017 su un database appena creato non è riuscito in poco più di un minuto (dopo la creazione di 50.931 tabelle)

Messaggio 2714, livello 16, stato 30, riga 15 È già presente un oggetto denominato "PK__abcdefgh__3BD019A8175067CE" nel database. Messaggio 1750, livello 16, stato 1, riga 15 Impossibile creare vincolo o indice. Vedi gli errori precedenti.


11

Supponendo di avere 100 milioni di tabelle, calcolo meno di 1 su 1 trilione di probabilità di una collisione

Ricorda che questo è il " problema del compleanno ". Non stai provando a generare una collisione per un singolo hash dato, ma piuttosto a misurare la probabilità che nessuna delle tante coppie di valori entrerà in collisione.

Quindi, con N tabelle, ci sono N * (N-1) / 2 coppie, quindi qui circa 10 16 coppie. Se la probabilità di una collisione è 2 -64 , la probabilità che una singola coppia non si scontri è 1-2 -64 , ma con così tante coppie, la probabilità di non avere collisioni qui è di circa (1-2 -64 ) 10 16 o più come 1 / 10.000. Vedi ad esempio https://preshing.com/20110504/hash-collision-probabilities/

E se si tratta solo di un hash a 32 bit, la probabilità di una collisione attraversa 1/2 a soli 77k valori.


2
E per arrivare a 77K valori in primo luogo senza incontrare una collisione è probabilmente abbastanza improbabile in quanto è necessario essere stato fortunato per tutte le precedenti creazioni prima di quello. Mi chiedo quale sia il punto in cui la probabilità cumulativa di una collisione raggiunge il 50%
Martin Smith,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.