SQL Server peut-il créer des collisions dans les noms de contraintes générés par le système?

14

J'ai une application qui crée des millions de tables dans une base de données SQL Server 2008 (non en cluster). Je cherche à mettre à niveau vers SQL Server 2014 (en cluster), mais je frappe un message d'erreur lorsqu'il est en charge:

"Il y a déjà un objet nommé 'PK__tablenameprefix__179E2ED8F259C33B' dans la base de données"

Il s'agit d'un nom de contrainte généré par le système. Il ressemble à un nombre 64 bits généré de manière aléatoire. Est-il possible que je constate des collisions en raison du grand nombre de tables? En supposant que j'ai 100 millions de tables, je calcule moins d'une chance de collision de 1 sur 1 trillion lors de l'ajout de la table suivante, mais cela suppose une distribution uniforme. Est-il possible que SQL Server ait changé son algorithme de génération de nom entre la version 2008 et 2014 pour augmenter les chances de collision?

L'autre différence significative est que mon instance de 2014 est une paire en cluster, mais j'ai du mal à formuler une hypothèse pour expliquer pourquoi cela générerait l'erreur ci-dessus.

PS Oui, je sais que créer des millions de tables est fou. Il s'agit d'un code tiers de boîte noire sur lequel je n'ai aucun contrôle. Malgré la folie, cela a fonctionné dans la version 2008 et maintenant pas dans la version 2014.

Edit: en y regardant de plus près, le suffixe généré semble toujours commencer par 179E2ED8 - ce qui signifie que la partie aléatoire n'est en fait qu'un nombre de 32 bits et que les chances de collision ne sont que de 1 sur 50 à chaque fois qu'une nouvelle table est ajoutée, ce qui correspond beaucoup plus au taux d'erreur que je constate!

jl6
la source
Les noms de table sont différents mais ils utilisent une convention de dénomination qui fait qu'au moins les 11 premiers caractères sont les mêmes, et cela semble être tout ce que SQL Server utilise pour générer le nom de la contrainte.
jl6
Le matériel sous-jacent est différent (nouvelle génération de DL380) mais pas des performances significativement plus élevées. Le but de l'exercice est de remplacer SQL Server 2008 hors prise en charge, et non d'améliorer le débit, et le matériel a été provisionné en conséquence.
jl6

Réponses:

15

SQL Server peut-il créer des collisions dans les noms de contraintes générés par le système?

Cela dépend du type de contrainte et de la version de 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

Exemple de résultats 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           |
+--------------------------+-----------+---------------+----------------------+

Exemple de résultats 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           |
+--------------------------+------------+---------------+----------------------+

Pour les contraintes par défaut, les contraintes de vérification et les contraintes de clé étrangère, les 4 derniers octets du nom généré automatiquement sont une version hexadécimale de l'ID d'objet de la contrainte. Comme ils objectidsont garantis uniques, le nom doit également être unique. Dans Sybase aussi, ceux-ci utilisenttabname_colname_objectid

Pour les contraintes uniques et les contraintes de clé primaire utilisées par Sybase

tabname_colname_tabindid, où tabindid est une concaténation de chaîne de l'ID de table et de l'ID d'index

Cela aussi garantirait l'unicité.

SQL Server n'utilise pas ce schéma.

Dans SQL Server 2008 et 2017, il utilise une chaîne de 8 octets à la fin du nom généré par le système, mais l'algorithme a changé la façon dont les 4 derniers octets sont générés.

En 2008, les 4 derniers octets représentent un compteur d'entier signé qui est décalé du object_idpar -16000057avec toute valeur négative enveloppant autour de max signé int. (L'importance de 16000057est qu'il s'agit de l'incrément appliqué entre les créations successivesobject_id ). Cela garantit toujours l'unicité.

À partir de 2012, je ne vois aucun modèle entre l'id_objet de la contrainte et l'entier obtenu en traitant les 8 derniers caractères du nom comme la représentation hexadécimale d'un int signé.

Les noms de fonction dans la pile d'appels en 2017 montrent qu'il crée maintenant un GUID dans le cadre du processus de génération de nom (en 2008, je ne vois aucune mention MDConstraintNameGenerator). Je suppose que c'est pour fournir une source de hasard. De toute évidence, il n'utilise pas l'ensemble des 16 octets du GUID dans ces 4 octets qui changent entre les contraintes cependant.

entrez la description du lien ici

Je suppose que le nouvel algorithme a été fait pour une raison d'efficacité au détriment d'une possibilité accrue de collisions dans des cas extrêmes tels que le vôtre.

C'est tout à fait un cas pathologique car il nécessite que le préfixe du nom de table et le nom de colonne du PK (dans la mesure où cela affecte les 8 caractères précédant le 8 final) soient identiques pour des dizaines de milliers de tableaux avant qu'il ne devienne probable mais peut être reproduit assez facilement avec ci-dessous.

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 exemple exécuté sur SQL Server 2017 sur une base de données nouvellement créée a échoué en un peu plus d'une minute (après la création de 50 931 tables)

Msg 2714, niveau 16, état 30, ligne 15 Il existe déjà un objet nommé «PK__abcdefgh__3BD019A8175067CE» dans la base de données. Msg 1750, niveau 16, état 1, ligne 15 Impossible de créer une contrainte ou un index. Voir les erreurs précédentes.

Martin Smith
la source
11

En supposant que j'ai 100 millions de tables, je calcule moins d'une chance sur 1 billion de collision

N'oubliez pas que c'est le " problème d'anniversaire ". Vous n'essayez pas de générer une collision pour un hachage donné, mais plutôt de mesurer la probabilité qu'aucune des nombreuses paires de valeurs n'entre en collision.

Donc avec N tables, il y a N * (N-1) / 2 paires, donc ici environ 10 16 paires. Si la probabilité d'une collision est de 2 à 64 , la probabilité qu'une seule paire ne se heurte pas est de 1 à 2 -64 , mais avec autant de paires, la probabilité de ne pas avoir de collision ici est d'environ (1-2 à 64 ) 10 16 , ou plus comme 1/10 000. Voir par exemple https://preshing.com/20110504/hash-collision-probabilities/

Et s'il ne s'agit que d'un hachage 32 bits, la probabilité d'une collision passe de 1/2 à seulement 77k valeurs.

David Browne - Microsoft
la source
2
Et obtenir des valeurs de 77K en premier lieu sans rencontrer de collision est probablement assez improbable car vous devez avoir été chanceux pour toutes les créations précédentes avant cela. Je me demande quel est le point où la probabilité cumulée d'une collision atteint 50%
Martin Smith