Utiliser un GUID comme clé primaire

32

J'utilise généralement des ID d'incrémentation automatique comme clés primaires dans les bases de données. J'essaie de découvrir les avantages de l'utilisation des GUID. J'ai lu cet article: https://betterexplained.com/articles/the-quick-guide-to-guids/

Je me rends compte que ces GUID sont utilisés pour identifier les objets au niveau de l'application. Sont-ils également stockés en tant que clé primaire au niveau de la base de données. Par exemple, disons que j'ai eu la classe suivante:

public class Person
{
public GUID ID;
public string Name;
..

//Person Methods follow
}

Disons que je voulais créer une nouvelle personne en mémoire, puis insérer la personne dans une base de données. Puis-je simplement faire ceci:

Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

Disons que j'avais une base de données contenant des millions et des millions de lignes avec un GUID comme clé primaire. Est-ce que ce sera toujours unique? Suis-je même comprendre correctement les GUID?

J'ai lu cet article plus tôt: http://enterprisecraftsmanship.com/2014/11/15/cqs-with-database-generated-ids/ . Cela m'embrouille un peu car il semble recommander un juste milieu entre les GUID et les entiers comme clés primaires.

Modifier le 11/06/18

J'en suis venu à croire que les Guids sont plus adaptés que les ints à mes besoins. J'utilise CQRS plus ces jours-ci et les GUID s'intègrent mieux.

Je remarque que certains développeurs modélisent les GUID sous forme de chaînes dans le modèle de domaine, par exemple ici: https://github.com/dotnet-architecture/eShopOnContainers/blob/dev/src/Services/Ordering/Ordering.Domain/AggregatesModel/BuyerAggregate/ Buyer.cs - dans ce cas: IdentityGuid est un GUID modélisé sous forme de chaîne. Y a-t-il une autre raison que celle indiquée ici: utiliser un objet de valeur personnalisé ou un GUID comme identifiant d'entité dans un système distribué? . Est-il «normal» de modéliser le GUID en tant que chaîne ou dois-je le modéliser en tant que GUID dans le modèle et la base de données?

w0051977
la source
7
Il n'est pas garanti d'être unique, mais il est peu probable que vous voyiez une collision. stackoverflow.com/questions/1155008/how-unique-is-uuid/…
icirellik
2
voir aussi: Collisions UUID
gnat
2
Voir aussi dba.stackexchange.com/questions/54690/… , ainsi que de nombreuses autres questions - ce sujet a souvent été posé, répondu et débattu.
Greenstone Walker
1
Le système avec lequel je travaille actuellement utilise des UUID. Une belle propriété est qu'un ID identifie de manière unique un enregistrement, par opposition à un ID séquentiel qui identifie un enregistrement dans cette table.
Justin

Réponses:

41

Les GUID sont par définition des "IDentifiants globalement uniques". Il existe un concept similaire mais légèrement différent en Java appelé UUID "Universally Unique IDentifiers". Les noms sont interchangeables pour toute utilisation pratique.

Les GUID sont au cœur de la façon dont Microsoft envisageait le clustering de bases de données, et si vous devez incorporer des données provenant de sources parfois connectées, elles aident vraiment à prévenir les collisions de données.

Quelques faits Pro-GUID:

  • Les GUID empêchent les collisions clés
  • Les GUID aident à fusionner des données entre réseaux, machines, etc.
  • SQL Server prend en charge les GUIDS semi-séquentiels pour aider à minimiser la fragmentation d'index ( réf , certaines mises en garde)

Une certaine laideur avec les GUID

  • Ils sont gros, 16 octets chacun
  • Ils sont hors service, vous ne pouvez donc pas trier sur l'ID et espérer obtenir l'ordre d'insertion comme vous le pouvez sur les ID d'incrémentation automatique
  • Ils sont plus lourds à utiliser, en particulier sur les petits ensembles de données (comme les tables de recherche)
  • La nouvelle implémentation GUID est plus robuste sur SQL Server que dans la bibliothèque C # (vous pouvez avoir des GUIDS séquentiels à partir de SQL Server, en C # c'est aléatoire)

Les GUID augmenteront la taille de vos index, donc le coût d'espace disque pour l'indexation d'une colonne sera plus élevé. Les GUID aléatoires fragmenteront vos index.

Si vous savez que vous n'allez pas synchroniser les données de différents réseaux, les GUID peuvent entraîner plus de surcharge qu'ils n'en valent.

Si vous avez besoin d'ingérer des données de clients parfois connectés, ils peuvent être beaucoup plus robustes pour empêcher les collisions de clés que de dépendre de la définition de plages de séquences pour ces clients.

Berin Loritsch
la source
18
Ma compréhension est que les GUID sont synonymes d'UUID. UUID est le nom standard. Le GUID est ce que Microsoft les a inventé avant RFC 4122 .
JimmyJames
13
"Ils ne fonctionnent pas, vous ne pouvez donc pas trier les identifiants et espérer obtenir l'ordre d'insertion comme vous le pouvez sur les identifiants à incrémentation automatique" Franchement, je ne suis pas à l'aise de compter sur cela avec des identifiants normaux non plus. Bien qu'il soit possible, dans un cas extrême, qu'un ID inférieur soit validé sur le disque plus tard, je préfère me fier à des données de tri utiles, comme un horodatage d'insertion. Les ID doivent être traités comme des adresses mémoire - tout en a une, mais la valeur elle-même n'a pas de sens. Utilisez-les pour les bris d'égalité tout au plus. D'autant plus que si vous avez un chargement en masse, l'ordre d'insertion n'est pas garanti.
Clockwork-Muse
8
@CortAmmon Selon Wikipedia et RFC 4122 , ils sont synonymes. P. Leach de Microsoft a été l'un des créateurs de la RFC. Je pense que depuis la création du RFC, les deux sont les mêmes. De la RFC: "UUID (Universally Unique IDentifier), également connu sous le nom de GUID (Globally Unique IDentifier)." Je pense qu'il est également utile de noter que les GUID n'ont pas été créés par MS. Ils viennent de créer un nouveau nom pour une technologie adoptée d'ailleurs.
JimmyJames
6
"SQL Server possède des optimisations pour gérer les GUID, de sorte qu'il ne devrait pas affecter beaucoup les performances des requêtes." -1 Pas assez optimisé. Je travaille avec une base de données où tous les PK sont des guids, et c'est l'une des principales causes de mauvaises performances.
Andy
7
"SQL Server a des optimisations pour gérer les GUID, donc cela ne devrait pas affecter beaucoup les performances des requêtes. " Pas vrai. Cette déclaration suppose que d'autres types de données ne sont pas optimisés. Les serveurs de base de données ont également des optimisations pour traiter des valeurs int simples, par exemple. Les GUID / UUID sont beaucoup plus lents que l'utilisation d'une valeur int de 4 octets. 16 octets ne seront jamais aussi rapides que 4 octets - en particulier sur une machine qui gère au plus 4 ou 8 octets en mode natif.
Andrew Henle
28

Est-ce que ce sera toujours unique?

Toujours? non, pas toujours; c'est une séquence finie de bits.

Disons que j'avais une base de données contenant des millions et des millions de lignes avec un GUID comme clé primaire.

Des millions et des millions, vous êtes probablement en sécurité. Un million de millions, et la probabilité d'une collision devient importante. Il y a cependant une bonne nouvelle: vous avez déjà épuisé l'espace disque au moment où cela se produit.

Puis-je simplement faire ça?

Vous pouvez; ce n'est pas une très bonne idée. Votre modèle de domaine ne devrait normalement pas générer de nombres aléatoires; ils doivent être des entrées pour votre modèle.

Au-delà de cela, lorsque vous traitez avec un réseau peu fiable, où vous pouvez obtenir des messages en double, un UUID généré de manière déterministe vous protégera contre la présence d'entités en double. Mais si vous attribuez un nouveau nombre aléatoire à chacun, vous avez plus de travail à faire pour identifier la duplication.

Voir la description de l'uuid basé sur le nom dans la RFC 4122

Est-il «normal» de modéliser le GUID en tant que chaîne ou dois-je le modéliser en tant que GUID dans le modèle et la base de données?

Je ne pense pas que cela compte beaucoup. Pour la plupart de votre modèle de domaine, c'est un identifiant ; la seule question que vous lui demandez est de savoir si elle est ou non identique à un autre identifiant. Votre modèle de domaine ne regarde normalement pas la représentation en mémoire d'un identifiant.

Si le GUID est disponible en tant que «type primitif» dans votre paramètre indépendant de domaine, je l'utilise; il permet au contexte de prise en charge de choisir les optimisations appropriées qui peuvent être disponibles.

Ce que vous devez reconnaître, cependant, c'est que la représentation de l'identifiant, à la fois en mémoire et en stockage, est une décision que vous prenez dans votre implémentation, et donc vous devez prendre des mesures pour vous assurer que l'empreinte de code couplée à celle-ci la décision est petite - voir Parnas 1972 .

VoiceOfUnreason
la source
20
+1 pour "vous avez déjà épuisé l'espace disque au moment où cela se produit."
w0051977
2
Je pense que le concept de " UUID généré de manière déterministe " est essentiel (voir Data Vault 2)
alk
En effet, pouvoir recalculer un UUID / GUID à partir d'autres données est une immense aide, notamment pour détecter les doublons. Une fois, j'ai construit un système de traitement des messages qui stockait les messages et les poussait à travers un pipeline de traitement. J'ai créé un hachage du message et l'ai utilisé comme clé primaire dans tout le système. juste que, en soi, m'a résolu BEAUCOUP de problèmes pour identifier le message lorsque nous devions évoluer.
Newtopian
Un million de millions = 2 ^ 40. Cela fait 2 ^ 79 paires de collisions possibles. Le GUID a 2 ^ 128 bits, donc la chance est de 1 sur 2 ^ 49. Il est beaucoup plus probable que vous ayez un bogue qui réutilise le même GUID pour deux enregistrements, ou qui pense à tort qu'il y a une collision là où il n'y en a pas.
gnasher729
Je reviens sur mes questions historiques. Avant d'accepter; pourriez-vous jeter un oeil à mon montage?
w0051977
11

Le GUID ou l' UUID sera très probablement unique en raison de la façon dont ils sont générés et ils fournissent un moyen sûr de garantir l'unicité sans avoir à communiquer avec une autorité centrale.

Avantages des GUID en tant que clé primaire:

  • Vous pouvez copier des données entre différents fragments d'un cluster et ne pas avoir à vous soucier des collisions PK.
  • Il vous permet de connaître votre clé primaire avant d'avoir inséré des enregistrements.
  • Simplifie la logique de transaction pour l'insertion d'enregistrements enfants.
  • Ne peut pas être facilement deviné.

Dans l'exemple que vous avez fourni:

Person p1 = new Person();
p1.ID = GUID.NewGUID();
PersonRepository.Insert(p1);

La spécification du GUID avant l'heure d'insertion peut enregistrer un aller-retour dans la base de données lors de l'insertion d'enregistrements enfants successifs et vous permettre de les valider dans la même transaction.

Person p2 = new Person();
p2.ParentID = p1.ID
PersonRepository.Insert(p2);

Les inconvénients des GUID en tant que clé primaire:

  • Ils sont grands de 16 octets, ce qui signifie qu'ils consomment plus d'espace à mesure que les index et les clés étrangères sont ajoutés.
  • Ils ne trient pas bien car ce sont essentiellement des nombres aléatoires.
  • L'utilisation de l'index est très, très, très mauvaise.
  • Beaucoup de feuilles qui bougent.
  • Ils sont difficiles à retenir.
  • Ils sont difficiles à verbaliser.
  • Ils peuvent rendre les URL plus difficiles à lire.

Si votre application n'a pas besoin de partitionnement ou de clustering, il serait préférable de s'en tenir à des types de données plus petits et plus simples tels que int ou bigint.

De nombreuses bases de données ont leurs propres implémentations internes qui tentent d'atténuer les problèmes de stockage causés par les GUID et SQL Server a même une fonction newsequentialid pour aider à la commande des UUID permettant une meilleure utilisation des index et elles ont généralement de meilleures caractéristiques de performances.

De plus, du point de vue d'un testeur, d'un utilisateur ou d'un développeur travaillant avec l'application, l'utilisation d'un ID sur un GUID améliorera considérablement la communication. Imaginez avoir à lire un GUID sur un téléphone.

En fin de compte, à moins que le clustering à grande échelle ou l'URL d'obscurcissement ne soit une exigence, il est plus pragmatique de s'en tenir aux ID à incrémentation automatique.

icirellik
la source
1
Une chose à considérer est que, selon le type d' UUID , ils contiennent des informations qui pourraient potentiellement être utilisées pour identifier la machine sur laquelle ils sont générés. La variante aléatoire pure peut être plus susceptible d'entrer en collision sans entropie suffisante. Cela doit être pris en compte avant utilisation dans un URI.
JimmyJames
D'accord, mais il ne faut jamais exposer leur clé primaire dans une URL. Une méthode plus appropriée devrait être utilisée pour s'assurer qu'il n'y a pas de données sécurisées qui fuient vers le système externe.
icirellik
1
Il existe un autre cas d'utilisation: les bases de données OLTP à insertion lourde dans lesquelles le verrouillage de la séquence est un goulot d'étranglement. Selon mon ami Oracle DBA, ce n'est pas aussi rare qu'il y paraît, vous n'avez même pas besoin de grandes échelles ou de clusters pour cela. • En fin de compte, pesez le pour et le contre (et ne confondez pas le pour / le contre des UUID avec le pour / le contre qui n'est pas spécifique aux UUID comme le font certaines affiches) et mesurez .
mirabilos
1
Si vous utilisez newsequentialid, vous devez vous rendre sur la base de données pour obtenir l'identifiant (comme avec une identité int), n'est-ce pas? Quel est l'avantage ici.
w0051977
1
@mirabilos Pour être clair, quand je dis horrible, nous avons fini par avoir des encarts qui prenaient des minutes par ligne. Tout a commencé bien, mais après qu'il y ait eu des dizaines de milliers de rangées, il est allé très vite latéralement. Si ce n'est pas évident, 10s de milliers de lignes est un très petit tableau.
JimmyJames
4

Je dirais non, n'utilisez pas les GUID comme clés primaires. Je suis en fait confronté à une telle base de données maintenant, et ils sont l'une des principales causes de problèmes de performances.

Les 12 octets supplémentaires s'additionnent rapidement; rappelez-vous, la plupart des PK seront des FK dans d'autres tables, et seulement trois FK dans une table vous avez maintenant 48 octets supplémentaires pour chaque ligne. Cela s'additionne dans la table et dans les index. Il s'additionne également dans les E / S disque. Ces 12 octets supplémentaires doivent être lus et écrits.

Et si vous n'utilisez pas de GUID séquentiels et que les PK sont regroupés (ce qui se produit par défaut), SQL devra de temps en temps déplacer des pages entières de données pour les presser davantage au bon endroit. Pour une base de données hautement transactionnelle avec beaucoup d'insertions, de mises à jour et de suppressions, les choses s'enlisent rapidement.

Si vous avez besoin d'une sorte d'identifiant unique pour la synchronisation ou quelque chose, ajoutez une colonne guid. Il suffit de ne pas en faire le PK.

Andy
la source
4
Person p1 = new Person();
p1.ID=GUID.NewGUID();
PersonRepository.Insert(p1);

C'est de loin la raison la plus importante pour l'utilisation des GUID.

Le fait que vous puissiez créer un identifiant unique sans que votre code connaisse ou communique avec votre couche de persistance est un énorme avantage.

Vous pouvez être sûr que l'objet Personne que vous venez de générer sur votre serveur, téléphone pc, ordinateur portable, appareil hors ligne ou tout ce qui est unique sur tous vos serveurs partout dans le monde, quelle que soit sa distribution.

Vous pouvez le coller dans n'importe quel type de base de données rdb ou no-sql, fichier, l'envoyer à n'importe quel service Web ou le jeter immédiatement comme inédit

Non, vous n'obtiendrez jamais de collision.

Oui, les inserts peuvent être légèrement plus lents car il peut être nécessaire de manipuler l'index.

Oui, c'est plus grand qu'un int.

  • modifier. a dû tirer avant de terminer.

Je sais que beaucoup de gens sont très attachés aux pouces auto et c'est un sujet controversé avec les DBA

Mais je ne peux vraiment pas dire assez clairement à quel point les guides sont supérieurs. Vous devez utiliser des guides par défaut dans n'importe quelle application.

les incs automobiles ont de nombreux défauts

  • Vous utilisez une base de données distribuée sans SQL. Vous ne pouvez tout simplement pas parler à toutes les autres instances pour savoir quel est le prochain numéro.

  • Vous utilisez un système de file d'attente de messages. Les choses ont besoin d'identifiants avant de toucher la base de données

  • Vous créez plusieurs éléments et les modifiez avant d'enregistrer. Chacun a besoin d'un identifiant avant d'avoir touché la base de données

  • Vous souhaitez supprimer et réinsérer des lignes. Assurez-vous de ne pas compter vos identifiants automatiques et de manquer!

  • Vous ne voulez pas exposer le nombre de commandes que vous avez prises cette année à chaque utilisateur

  • Vous souhaitez déplacer les données anonymisées de la production vers les tests et conserver les relations intactes. Mais ne supprimez pas toutes les données de test existantes.

  • Vous souhaitez fusionner votre produit à locataire unique dans une base de données à locataires multiples, mais tout le monde a une commande 56.

  • Vous créez des objets persistants mais éphémères. (commandes incomplètes) encore une fois, n'utilisez pas toutes vos ints avec des trucs qui n'existent plus.

La liste est interminable et ce sont tous de vrais problèmes qui arrivent aux gens tout le temps. contrairement à manquer d'espace disque en raison de cols FK légèrement plus grands

Enfin, le problème majeur avec les pouces est que vous en manquez !!! ok en théorie vous ne le faites pas, il y a des charges. Mais en pratique, vous le faites parce que les gens ne les traitent pas comme des nombres aléatoires sans signification. ils font des choses comme

  • oh je ne veux pas que les clients pensent que nous sommes nouveaux. commencer à 10000

  • J'ai dû importer une charge de données donc j'ai juste augmenté la graine à 1 m pour que nous sachions ce qui est importé

  • nous avons besoin de catégories de données. chaque période commence au million suivant afin que nous puissions utiliser les premiers chiffres comme un nombre magique

  • J'ai supprimé et réimporté à nouveau toutes les données avec de nouveaux identifiants. Oui, même les journaux d'audit.

  • utiliser ce numéro, qui est une clé composite, comme id de cette autre chose

Ewan
la source
1
Il n'y a rien de mal à cette réponse, mais je voudrais (pour éloigner les votes négatifs) expliciter peut-être que même si les applications réelles ne rencontrent pas de collisions, c'est théoriquement possible. (Ou peut-être plus de 45 bases de données exaoctets sont plus répandues que je ne le pensais ...). Bien que je pense que le langage "la raison la plus importante" est un peu fort, c'est ce que je trouve le plus utile.
BurnsBA
2
il est plus probable qu'un auto inc int entre en collision qu'un guid
Ewan
4
-1 pour "Vous devez utiliser des guides par défaut dans n'importe quelle application." Ça dépend ™. Et comme d'autres l'ont montré, les GUID / UUID ne sont absolument pas garantis d'être uniques.
Max Vernon
3
Les réponses "ça dépend" sont inutiles, bien sûr qu'il y aura des applications bizarres où un int est meilleur. Mais il y a de fortes chances que votre candidature n'en fasse pas partie. Les GUID sont la chose la plus unique que vous puissiez obtenir
Ewan
2
Je pense qu'il y aura quelques applications étranges où les guides sont meilleurs. Unique n'est pas la chose la plus importante à considérer. Vos «défauts» de poils sont massivement exagérés, et vous ne considérez aucun des nombreux inconvénients des guides.
Andy
2

Je me rends compte que ces GUID sont utilisés pour identifier les objets au niveau de l'application. Sont-ils également stockés en tant que clé primaire au niveau de la base de données.

C'est là que vous devez vous arrêter, juste là, et repenser.

La clé primaire de votre base de données ne doit JAMAIS avoir de sens commercial. Il devrait être dénué de sens par définition.

Ajoutez donc le GUID comme clé d'entreprise et une clé primaire normale (généralement un entier long) comme clé primaire de la base de données. Vous pouvez toujours mettre un index unique sur le GUID pour garantir l'unicité.

Cela parle bien sûr de la théorie des bases de données, mais c'est également une bonne pratique. J'ai traité des bases de données où les clés primaires avaient une signification commerciale (un client avait pensé à économiser certaines ressources de base de données en les utilisant comme numéros d'employé, numéros de client, etc. etc. par exemple) et cela causait toujours des problèmes.

jwenting
la source
1
En quoi est-ce différent de l'interrogation à partir de la couche application à l'aide d'une clé primaire entière? À ce stade, il est également utilisé pour identifier les objets au niveau de la couche application. Vous avez en quelque sorte besoin d'un moyen d'identifier les objets d'une base de données à partir de la couche application.
icirellik
@icirellik la clé primaire est destinée à un usage interne par la base de données, pour lier les enregistrements parents et enfants et similaires. Il n'est PAS destiné à être utilisé par la logique d'application, vous utilisez des ID d'entreprise pour cela, comme un numéro de produit ou un nom.
jwenting
2

Utilisez toujours des clés primaires (PK) générées par base de données et auto-incrémentées.

Pourquoi utiliser l'incrémentation automatique au lieu de GUID / UUID?

  • Les GUID (UUID) n'empêchent pas les collisions de clés car elles ne sont pas uniques et il n'y a aucun moyen de les rendre uniques car elles sont générées à partir de nombreuses sources.
  • Les GUID ne facilitent pas la fusion car ils augmentent considérablement le processus de fusion, déjà long, avec des colonnes PK et FK extrêmement longues et non entières qui prennent beaucoup de temps à traiter. N'oubliez pas que pour la plupart des PK, il y aura au moins 1 autre table avec au moins 2 clés de la même taille: c'est son propre PK et un FK de retour à la première table. Tous doivent être résolus dans une fusion.

Mais comment alors gérer les éclats, les clusters, etc.?

  • Créez des PK multi-colonnes composés de colonnes distinctes identifiant chaque fragment / cluster / base de données / tout ce qui gère ses propres clés d'incrémentation automatique. Par exemple...

Un PK à 3 colonnes pour une table en cluster peut être ...

 DB | SH | KEY     |
----|----|---------|
 01 | 01 | 1234567 |

Mais qu'en est-il de ...?

  • Voyages multiples dans la base de données - La plupart des applications n'ont pas besoin d'identifier de manière unique un enregistrement en cours de création jusqu'à ce qu'il soit inséré dans la base de données, car ce thread / session / tout ce qui ne fonctionne que sur un à la fois. Si l'application a vraiment besoin de cette capacité, utilisez un PK temporaire généré par l'application qui n'est pas envoyé à la base de données . Laissez la base de données mettre ensuite son propre PK à incrémentation automatique sur la ligne lorsqu'elle est insérée. Les insertions utiliseront le PK temporaire, tandis que les mises à jour et les suppressions utiliseront le PK permanent attribué par la base de données.

  • Performances - Les ordinateurs peuvent traiter des entiers simples beaucoup plus rapidement que toute autre chose en raison du domaine beaucoup plus grand si possible des valeurs par élément dans un GUID (37) par rapport à un entier (10). N'oubliez pas non plus que chaque caractère d'un GUID doit d'abord être converti en nombre pour être manipulé par le CPU.

Mauvais usage courant des clés primaires Les PK n'ont qu'un seul but ... identifier de manière absolument unique une ligne dans une table. Tout le reste est une mauvaise utilisation trop courante.

Détection des enregistrements manquants

  • Les enregistrements manquants ne peuvent pas être détectés en regardant les PK. Bénissez QA pour au moins avoir tenté d'assurer la qualité des données. Cependant, eux et le manque de compréhension du programmeur sur la façon dont les clés des systèmes de base de données modernes sont attribués les conduisent souvent à croire qu'un numéro manquant dans un PK à incrémentation automatique signifie des données manquantes. Ce n'est pas parce que ...
  • Pour des performances optimales, les systèmes de base de données allouent des blocs de nombres en «séquences» (lots, plages) pour minimiser les déplacements vers la base de données réelle en stockage. La taille de ces séquences de nombres est souvent sous le contrôle du DBA mais peut ne pas être ajustable sur une base par table.
  • La clé à retenir est que ... les numéros inutilisés de ces séquences ne sont jamais renvoyés dans la base de données, il y a donc toujours des lacunes dans les numéros PK.
  • Pourquoi y aurait-il des numéros inutilisés que vous demandez? Parce qu'une variété d'actions de maintenance de la base de données peut entraîner l'abandon de séquences. Ce sont des choses comme les redémarrages, les rechargements en masse de tables, certains types de restauration à partir de sauvegardes et d'autres opérations.

Tri

  • Le tri par PK est très sujet aux erreurs car la plupart des gens penseront qu'il répertorie les lignes dans l'ordre où elles ont été créées et que cela correspond à l'heure de l'horloge. Surtout, mais pas nécessairement.
  • Les moteurs de base de données sont optimisés pour des performances maximales et cela peut signifier retarder l'insertion des résultats d'une transaction compliquée de longue durée afin d'en insérer de simples simples, "hors-tour" pour ainsi dire.
DocSalvager
la source
Que pensez-vous du schéma de table de telle sorte que la seule colonne unique soit une clé primaire à incrémentation automatique créée par la base de données? En particulier pour les tables qui n'ont pas de clé étrangère mais dont la clé primaire est la clé étrangère pour plusieurs tables liées?
RibaldEddie
J'ai ajouté beaucoup plus à la réponse dans ce sens. La réponse originale était incomplète en raison de l'application Android SE que je suspendais. Je pense qu'une réécriture majeure de l'application est en cours de développement.
DocSalvager
Donc, à votre avis, il serait acceptable qu'une table contienne un nombre quelconque de lignes identiques, sauf pour leur clé primaire à incrémentation automatique?
RibaldEddie
@RibaldEddie - En ce qui concerne ce que la base de données est conçue pour permettre ... absolument. Les suppressions sont faciles. Lorsque votre scénario se produit, je considère qu'il s'agit d'un bogue à corriger dans le logiciel, puis je supprime l'une ou l'autre ligne. Le cas beaucoup plus courant est cependant deux enregistrements pour la même chose avec des données légèrement différentes, ils doivent donc être fusionnés. Si une colonne est vide dans un enregistrement et a une valeur dans l'autre, le choix est évident et peut être automatisé. Souvent, l'horodatage peut être utilisé pour arbitrer une fusion automatisée. Certains doublons nécessitent qu'une personne termine et vérifie la fusion en fonction des règles métier.
DocSalvager
1

Comme tout, il y a des avantages et des inconvénients à le faire:

Le bon:

  1. Vos clés ont toujours la même longueur (les très grandes bases de données peuvent avoir de très grandes clés)

  2. L'unicité est à peu près garantie - même lorsque vous les générez à partir d'un système distinct et / ou que vous n'avez pas lu le dernier ID de la base de données

Le mauvais:

  1. Comme mentionné ci-dessus - des index et un magasin de données plus importants.

  2. Vous ne pouvez pas commander par ID, vous devez commander par autre chose. Plus d'index, probablement moins efficaces.

  3. Ils sont moins lisibles par l'homme. Les entiers sont généralement plus faciles à analyser, à mémoriser et à taper pour les gens. L'utilisation de GUID comme ID dans des clauses WHERE sur plusieurs tables jointes peut faire fondre votre tête.

Comme tout, utilisez-les le cas échéant, ne soyez pas dogmatique - dans de nombreuses situations, les entiers à incrémentation automatique sont meilleurs, parfois les GUID sont excellents.

Phil S
la source
0

Oui, vous pouvez utiliser le GUID comme clé primaire. L'inconvénient est la taille et la fragmentation rapide de l'indice.

Sauf si vous avez besoin d'unicité entre les bases de données (par exemple un cluster), l'entier est préférable.

paparazzo
la source
Les générateurs GUID peuvent produire le même GUID plus d'une fois, c'est là que réside un défaut. Qu'ils le soient ou non dépend de leur granularité, principalement de l'intervalle entre les tics d'horloge. Par exemple, un générateur basé sur une horloge ne peut cocher que toutes les 100 ms, ce qui conduit à ce que 2 GUID demandés dans les 100 ms sur cette machine soient identiques. Il existe des moyens d'éviter cela, principalement, mais de nombreux générateurs GUID fonctionnent entièrement à partir de l'adresse IP et / ou de l'adresse MAC et d'un horodatage.
jwenting
0

Voici mon point de vue sur ce problème - la solution est à mi-chemin entre le GUID et les valeurs int, en prenant le meilleur des deux.

La classe génère une valeur Id pseudo aléatoire (mais augmentant avec le temps), qui est similaire à un GUID Comb .

Le principal avantage est qu'il permet de générer des valeurs Id sur le client, plutôt que d'utiliser des valeurs d'auto-incrémentation générées sur le serveur (ce qui nécessite un aller-retour) avec un risque presque nul de valeurs dupliquées.

Les valeurs générées n'utilisent que 8 octets au lieu de 16 pour un GUID, et ne dépendent pas d'un ordre de tri de base de données spécifique (par exemple Sql Server for GUIDs ). Les valeurs pourraient être développées pour utiliser toute la longue plage non signée, mais cela entraînerait des problèmes avec toute base de données ou autre référentiel de données qui ne possède que des types entiers signés.

public static class LongIdGenerator
{
    // set the start date to an appropriate value for your implementation 
    // DO NOT change this once any application that uses this functionality is live, otherwise existing Id values will lose their implied date
    private static readonly DateTime PeriodStartDate = new DateTime(2017, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private static readonly DateTime PeriodEndDate = PeriodStartDate.AddYears(100);
    private static readonly long PeriodStartTicks = PeriodStartDate.Ticks;
    private static readonly long PeriodEndTicks = PeriodEndDate.Ticks;
    private static readonly long TotalPeriodTicks = PeriodEndTicks - PeriodStartTicks;

    // ensures that generated Ids are always positve
    private const long SEQUENCE_PART_PERMUTATIONS = 0x7FFFFFFFFFFF; 

    private static readonly Random Random = new Random();

    private static readonly object Lock = new object();
    private static long _lastSequencePart;

    public static long GetNewId()
    {
        var sequencePart = GetSequenceValueForDateTime(DateTime.UtcNow);

        // extra check, just in case we manage to call GetNewId() twice before enough ticks have passed to increment the sequence 
        lock (Lock)
        {
            if (sequencePart <= _lastSequencePart)
                sequencePart = _lastSequencePart + 1;

            _lastSequencePart = sequencePart;
        }

        // shift so that the sequence part fills the most significant 6 bytes of the result value
        sequencePart = (sequencePart << 16);

        // randomize the lowest 2 bytes of the result, just in case two different client PCs call GetNewId() at exactly the same time
        var randomPart = Random.Next() & 0xFFFF;

        return sequencePart + randomPart;
    }

    // used if you want to generate an Id value for a historic time point (within the start and end dates)
    // there are no checks, compared to calls to GetNewId(), but the chances of colliding values are still almost zero
    public static long GetIdForDateTime(DateTime dt)
    {
        if (dt < PeriodStartDate || dt > PeriodStartDate)
            throw new ArgumentException($"value must be in the range {PeriodStartDate:dd MMM yyyy} - {PeriodEndDate:dd MMM yyyy}");

        var sequencePart = GetSequenceValueForDateTime(dt.ToUniversalTime());
        var randomPart = Random.Next() & 0xFFFF;
        return ( sequencePart << 16 ) + randomPart;
    }

    // Get a 6 byte sequence value from the specified date time - startDate => 0 --> endDate => 0x7FFFFFFFFFFF
    // For a 100 year time period, 1 unit of the sequence corresponds to about 0.022 ms
    private static long GetSequenceValueForDateTime(DateTime dt)
    {
        var ticksFromStart = dt.ToUniversalTime().Ticks - PeriodStartTicks;
        var proportionOfPeriod = (decimal)ticksFromStart / TotalPeriodTicks;
        var result = proportionOfPeriod * SEQUENCE_PART_PERMUTATIONS;
        return (long)result;
    }

    public static DateTime GetDateTimeForId(long value)
    {
        // strip off the random part - the two lowest bytes
        var timePart = value >> 16;
        var proportionOfTotalPeriod = (decimal) timePart / SEQUENCE_PART_PERMUTATIONS;
        var ticks = (long)(proportionOfTotalPeriod * TotalPeriodTicks);
        var result = PeriodStartDate.AddTicks(ticks);
        return result;
    }
}
Peregrine
la source