Quelles sont les meilleures pratiques pour utiliser un GUID comme clé primaire, en particulier en ce qui concerne les performances?

336

J'ai une application qui utilise le GUID comme clé primaire dans presque toutes les tables et j'ai lu qu'il y a des problèmes de performances lors de l'utilisation du GUID comme clé primaire. Honnêtement, je n'ai vu aucun problème, mais je suis sur le point de démarrer une nouvelle application et je veux toujours utiliser les GUID comme clés primaires, mais je pensais utiliser une clé primaire composite (le GUID et peut-être un autre champ .)

J'utilise un GUID car ils sont agréables et faciles à gérer lorsque vous avez différents environnements tels que les bases de données "production", "test" et "dev", ainsi que pour les données de migration entre les bases de données.

J'utiliserai Entity Framework 4.3 et je veux attribuer le Guid dans le code d'application, avant de l'insérer dans la base de données. (c'est-à-dire que je ne veux pas laisser SQL générer le Guid).

Quelle est la meilleure pratique pour créer des clés primaires basées sur GUID, afin d'éviter les supposés résultats de performance associés à cette approche?

VAAA
la source
20
Le problème n'est pas supposé. Si votre PK est mis en cluster, presque chaque insert peut provoquer un fractionnement de page. Dans les versions modernes de SQL Server, cela était "corrigé" avec NEWSEQUENTIALID (), mais cela perd l'avantage de pouvoir le calculer à l'avance. Je vous recommande fortement de lire sur les GUID ailleurs car c'est une question beaucoup trop large et sollicitera probablement une bataille religieuse qui durera des heures ...
Aaron Bertrand
4
J'ajouterais également que le mot serveur est ambigu dans Je veux attribuer le Guid côté serveur ( je ne veux pas laisser SQL créer le GUID) .
Erik Philips
Cette question a des similitudes avec ce "sql-server-guid-sort-algorithm-why" stackoverflow.com/questions/7810602/…
Clinton Ward

Réponses:

495

Les GUID peuvent sembler être un choix naturel pour votre clé primaire - et si vous le devez vraiment, vous pourriez probablement argumenter pour l'utiliser pour la CLÉ PRIMAIRE de la table. Ce que je recommande fortement de ne pas faire, c'est d'utiliser la colonne GUID comme clé de clustering , ce que SQL Server fait par défaut, sauf si vous le lui dites expressément.

Vous devez vraiment séparer deux problèmes:

  1. la clé primaire est une construction logique - l'une des clés candidates qui identifie de manière unique et fiable chaque ligne de votre table. Cela peut être n'importe quoi, vraiment - une INT, une GUID, une chaîne - choisissez ce qui a le plus de sens pour votre scénario.

  2. la clé de clustering (la ou les colonnes qui définissent l '"index clusterisé" sur la table) - c'est une chose liée au stockage physique , et ici, un petit type de données stable et en constante augmentation est votre meilleur choix - INTou BIGINTcomme votre option par défaut.

Par défaut, la clé primaire d'une table SQL Server est également utilisée comme clé de cluster - mais cela n'a pas besoin d'être ainsi! J'ai personnellement constaté des gains de performances massifs lors de la division de la clé primaire / cluster basée sur GUID précédente en deux clés distinctes - la clé primaire (logique) sur le GUID et la clé de clustering (commande) sur une INT IDENTITY(1,1)colonne distincte .

Comme Kimberly Tripp - la reine de l'indexation - et d'autres l'ont dit à maintes reprises - GUIDcar la clé de clustering n'est pas optimale, car en raison de son caractère aléatoire, elle entraînera une fragmentation massive des pages et des index et des performances généralement mauvaises.

Oui, je sais - il y a newsequentialid()dans SQL Server 2005 et plus - mais même cela n'est pas vraiment et entièrement séquentiel et souffre donc également des mêmes problèmes que le GUID- un peu moins bien en évidence.

Ensuite, il y a un autre problème à considérer: la clé de clustering d'une table sera ajoutée à chaque entrée de chaque index non clusterisé de votre table également - vous devez donc vraiment vous assurer qu'elle est aussi petite que possible. En règle générale, un INTavec plus de 2 milliards de lignes devrait être suffisant pour la grande majorité des tables - et par rapport à un GUIDcomme clé de clustering, vous pouvez vous épargner des centaines de mégaoctets de stockage sur disque et dans la mémoire du serveur.

Calcul rapide - en utilisant INTvs GUIDcomme clé principale et de clustering:

  • Table de base avec 1'000'000 lignes (3,8 Mo contre 15,26 Mo)
  • 6 index non clusterisés (22,89 Mo contre 91,55 Mo)

TOTAL: 25 Mo contre 106 Mo - et ce n'est que sur une seule table!

Un peu plus de matière à réflexion - d'excellentes choses par Kimberly Tripp - lisez-le, relisez-le, digérez-le! C'est vraiment l'évangile d'indexation de SQL Server.

PS: bien sûr, si vous ne traitez qu'avec quelques centaines ou quelques milliers de lignes - la plupart de ces arguments n'auront pas vraiment d'impact sur vous. Cependant: si vous entrez dans les dizaines ou les centaines de milliers de lignes, ou si vous commencez à compter des millions - alors ces points deviennent très cruciaux et très importants à comprendre.

Mise à jour: si vous voulez avoir votre PKGUIDcolonne comme clé primaire (mais pas votre clé de cluster), et une autre colonne MYINT( INT IDENTITY) comme clé de cluster - utilisez ceci:

CREATE TABLE dbo.MyTable
(PKGUID UNIQUEIDENTIFIER NOT NULL,
 MyINT INT IDENTITY(1,1) NOT NULL,
 .... add more columns as needed ...... )

ALTER TABLE dbo.MyTable
ADD CONSTRAINT PK_MyTable
PRIMARY KEY NONCLUSTERED (PKGUID)

CREATE UNIQUE CLUSTERED INDEX CIX_MyTable ON dbo.MyTable(MyINT)

Fondamentalement: il vous suffit de dire explicitement à la PRIMARY KEYcontrainte qu'elle est NONCLUSTERED(sinon elle est créée en tant qu'index cluster, par défaut) - puis vous créez un deuxième index défini commeCLUSTERED

Cela fonctionnera - et c'est une option valable si vous avez un système existant qui doit être "repensé" pour des performances. Pour un nouveau système, si vous partez de zéro et que vous n'êtes pas dans un scénario de réplication, je choisirais toujours ID INT IDENTITY(1,1)ma clé primaire en cluster - beaucoup plus efficace qu'autre chose!

marc_s
la source
2
C'est une excellente réponse, une chose que je mentionnerais est que pouvoir générer la clé avant l'insertion est souvent utile. L'utilisation de "newsequentialid ()" peut aider à la mise en cluster, mais cela nécessite un aller-retour supplémentaire vers SQL. Un autre avantage de l'approche "clé de substitution" est que vous pouvez générer de nouveaux identifiants, côté client, avec moins de problèmes de fragmentation d'index.
Andrew Theken
2
La façon dont je lis cela est qu'ayant à la fois une colonne d'identifiant unique non cluster et la colonne d'identité int, les FK devraient également être un identifiant unique? Si vous faites cela, quand utiliseriez-vous réellement la colonne d'identité directement, ou pas?
pinkfloydx33
2
Petite question, le GUID devrait-il maintenant être utilisé sur les jointures, ou l'identifiant int? Mon instinct me dit que le GUID doit être utilisé, mais je ne vois pas de problème technique en utilisant l'identifiant int ...
Nicolas Belley
3
@marc_s mais dans un scénario de réplication, si la colonne int est identité, ne devrions-nous pas utiliser le GUID car la colonne int peut se répéter sur tous les appareils?
Nicolas Belley
6
@Kipei: le principal problème est le SI vous avez une telle valeur naturelle - alors oui, vous pouvez l'utiliser comme clé primaire. MAIS : des valeurs comme DATETIMEpar exemple ne sont PAS utiles pour une clé de clustering, car elles ont une précision de 3,33 ms seulement, et donc des doublons peuvent exister. Donc, dans un tel cas, vous * avez toujours besoin d'un à la INT IDENTITYplace - par conséquent, j'utilise généralement cela par défaut, depuis frmo mes 20+ années d'expérience, une clé naturelle vraiment utilisable n'existe presque jamais vraiment ....
marc_s
51

J'utilise des GUID comme PK depuis 2005. Dans ce monde de bases de données distribuées, c'est absolument la meilleure façon de fusionner des données distribuées. Vous pouvez déclencher et oublier les tables de fusion sans vous soucier de la correspondance des entiers entre les tables jointes. Les jointures GUID peuvent être copiées sans aucun souci.

Voici ma configuration pour l'utilisation des GUID:

  1. PK = GUID. Les GUID sont indexés de la même manière que les chaînes, de sorte que les tables de lignes hautes (plus de 50 millions d'enregistrements) peuvent nécessiter un partitionnement de table ou d'autres techniques de performances. SQL Server devient extrêmement efficace, donc les problèmes de performances sont de moins en moins applicables.

  2. PK Guid est un indice NON clusterisé. N'indexez jamais un GUID en cluster, sauf s'il s'agit de NewSequentialID. Mais même dans ce cas, un redémarrage du serveur entraînera des interruptions importantes dans la commande.

  3. Ajoutez ClusterID Int à chaque table. C'est votre index CLUSTERED ... qui commande votre table.

  4. La jointure sur ClusterIDs (int) est plus efficace, mais je travaille avec 20-30 millions de tables d'enregistrement, donc la jointure sur GUIDs n'affecte pas visiblement les performances. Si vous voulez des performances maximales, utilisez le concept ClusterID comme clé primaire et rejoignez ClusterID.

Voici ma table d'email ...

CREATE TABLE [Core].[Email] (
    [EmailID]      UNIQUEIDENTIFIER CONSTRAINT [DF_Email_EmailID] DEFAULT (newsequentialid()) NOT NULL,        
    [EmailAddress] NVARCHAR (50)    CONSTRAINT [DF_Email_EmailAddress] DEFAULT ('') NOT NULL,        
    [CreatedDate]  DATETIME         CONSTRAINT [DF_Email_CreatedDate] DEFAULT (getutcdate()) NOT NULL,      
    [ClusterID] INT NOT NULL IDENTITY,
    CONSTRAINT [PK_Email] PRIMARY KEY NonCLUSTERED ([EmailID] ASC)
);
GO

CREATE UNIQUE CLUSTERED INDEX [IX_Email_ClusterID] ON [Core].[Email] ([ClusterID])
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_Email_EmailAddress] ON [Core].[Email] ([EmailAddress] Asc)
Robert J. Good
la source
Pourriez-vous expliquer la contrainte PK_Email? Pourquoi avez-vous ... Non clusterisé (EmailID ASC) au lieu de ... Non clusterisé (ClusterID ASC)?
Phil
2
Tu paries. Deux choses principales se passent avec les index: 1. Clustered on ClusterID - Ordonne votre table sur le disque (fragmentation 0%). 2. Non clusterisé sur EmailID - Indexe le champ EmailID pour accélérer les recherches d'ID GUID. Une recherche de champ GUID se comporte comme une chaîne, donc une recherche EmailID serait lente sans l'index.
Robert
@ RobertJ.Bood J'ai vu cette méthode discutée avant, c'est-à-dire l'ajout d'une clé int de substitution sur le cluster. Mais je ne trouve nulle part qui montre le gain de performances d'avoir un index cluster de clé de substitution par rapport à l'utilisation d'un tas. Avez-vous des liens vers des données de référence?
Dale K
1
Salut @DaleBurrell, l'index cluster est pour empêcher la fragmentation de la table. Le gain de performances se produit lorsque la table se développe naturellement dans l'ordre sur le disque, avec une faible fragmentation.
Robert
@ RobertJ.Good Est-ce une application Web? Qu'utilisez-vous dans les urls / hrefs? guid ou int?
dariol
10

Je développe actuellement une application web avec EF Core et voici le schéma que j'utilise:

Toutes mes classes (tables) et un int PK et FK. J'ai une colonne supplémentaire avec le type Guid (généré par le constructeur c #) avec un index non clusterisé dessus.

Toutes les jointures de table dans EF sont gérées via les clés int tandis que tous les accès depuis l'extérieur (contrôleurs) se font avec les Guids.

Cette solution permet de ne pas afficher les clés int sur les URL mais de garder le modèle rangé et rapide.

EricImhauser
la source
Y a-t-il quelque chose que vous devez faire pour configurer le pK entier en cluster, comme les annotations de données, ou est-il simplement configuré automatiquement?
Allen Wang
Quel nom de propriété utilisez-vous pour Guid one?
Trong Phan
3

Si vous utilisez le GUID comme clé primaire et créez un index clusterisé, je suggère d'utiliser la valeur par défaut NEWSEQUENTIALID () pour cela

AnandPhadke
la source
pourquoi ferais-tu ça?
Genuinefafa
3

Ce lien le dit mieux que moi et m'a aidé dans ma prise de décision. J'utilise généralement un int comme clé primaire, sauf si j'ai un besoin spécifique de ne pas le faire et je laisse également le serveur SQL générer / maintenir automatiquement ce champ, sauf si j'ai une raison spécifique de ne pas le faire. En réalité, les problèmes de performances doivent être déterminés en fonction de votre application spécifique. Il existe de nombreux facteurs en jeu ici, mais sans s'y limiter, la taille de base de données attendue, une indexation appropriée, une interrogation efficace, etc. Bien que les gens puissent être en désaccord, je pense que dans de nombreux scénarios, vous ne remarquerez pas de différence avec l'une ou l'autre option et vous devriez choisir ce qui est le plus approprié pour votre application et ce qui vous permet de développer plus facilement, plus rapidement et plus efficacement (si vous ne terminez jamais l'application quelle différence le reste fait :).

https://web.archive.org/web/20120812080710/http://databases.aspfaq.com/database/what-should-i-choose-for-my-primary-key.html

PS Je ne sais pas pourquoi vous utiliseriez un PK composite ou quel avantage vous pensez que cela vous apporterait.

Mat
la source
Entièrement d'accord!! Mais cela signifie que si j'ai un GUID en tant que PK ou un PK composite avec GUID et un autre champ, ce sera la même chose?
VAAA
1
Le PK (index) serait composé des deux colonnes, mais à moins que vous n'ayez une raison spécifique à votre entreprise, cela semble inutile.
Matt
1
BTW cette question est l'une des questions les plus polarisées et débattues et donc extrêmement difficile d'obtenir une réponse pour laquelle vous vous sentirez à 100% à l'aise. Les deux méthodes sont assorties de compromis, alors bonne chance :)
Matt
0

Le fait d'avoir des identifiants séquentiels rend beaucoup plus facile pour un pirate ou un mineur de données de compromettre votre site et vos données. Gardez cela à l'esprit lorsque vous choisissez un PK pour un site Web.

DaBlue
la source
Pouvez-vous fournir une logique ou des preuves pour étayer cette affirmation? J'ai du mal à voir comment un identifiant séquentiel pourrait compromettre la sécurité.
jonaglon le
Bien sûr, si vous savez que les numéros d'identification sont des nombres entiers, vous pouvez deviner les enregistrements séquentiels dans une base de données. Donc, si vous interrogez un seul élément, vous pouvez dire que l'élément suivant est pk + 1. Si vous avez des GUIDS aléatoires, il ne suivra pas de modèle. Il serait presque impossible d'interroger d'autres enregistrements que celui que vous avez interrogé précédemment (et connaître le PK).
DaBlue
1
Si un pirate peut interroger votre base de données, vous êtes déjà compromis, je ne vois pas comment les identifiants séquentiels aggravent la situation.
jonaglon
1
Si un utilisateur peut remplacer 1012 pour un autre numéro et voir les données qu'il ne devrait pas, alors il y a un problème de sécurité très grave, ce problème n'est pas causé par le choix de la clé primaire mais il est exacerbé par cela. Je comprends votre argument, merci de l'avoir expliqué.
jonaglon le
2
Vous pouvez utiliser un GUID pour localiser un enregistrement sur la page Web, qui n'est pas le PK de la table. L'utilisation d'un paramètre de requête dans un site Web ne doit pas définir la façon dont vous structurez votre schéma de base de données. Le PK n'a rien à voir avec l'entrée et les paramètres dans l'interface utilisateur ou le système backend.
Panos Roditakis