GUID séquentiel ou bigint pour la table de base de données «énorme» PK

14

Je sais que ce type de question revient souvent, mais je n'ai pas encore lu d'arguments convaincants pour m'aider à prendre cette décision. S'il vous plaît, supportez-moi!

J'ai une énorme base de données - elle augmente d'environ 10 000 000 d'enregistrements par jour. Les données sont relationnelles et pour des raisons de performances, je charge la table avec BULK COPY. Pour cette raison, je dois générer des clés pour les lignes et ne peux pas compter sur une colonne IDENTITY.

Un entier 64 bits - un bigint - est assez large pour moi, mais pour garantir l'unicité, j'ai besoin d'un générateur centralisé pour faire mes identifiants pour moi. J'ai actuellement un tel service de générateur qui permet à un service de réserver des numéros de séquence X et ne garantit aucune collision. Cependant, une conséquence de cela est que tous les services dont je dispose dépendent de ce seul générateur centralisé, et donc je suis limité dans la façon dont je peux distribuer mon système et je ne suis pas satisfait des autres dépendances (telles que la nécessité d'un accès au réseau) imposées par cette conception. Cela a parfois été un problème.

J'envisage maintenant d'utiliser des GUID séquentiels comme clés primaires (générées en externe pour SQL). Pour autant que j'ai pu le vérifier à partir de mes propres tests, le seul inconvénient est la surcharge d'espace disque d'un type de données plus large (qui est exacerbé par leur utilisation dans les index). Je n'ai vu aucun ralentissement perceptible des performances des requêtes, par rapport à l'alternative bigint. Le chargement de la table avec BULK COPY est légèrement plus lent, mais pas de beaucoup. Mes index basés sur GUID ne deviennent pas fragmentés grâce à mon implémentation séquentielle GUID.

Fondamentalement, ce que je veux savoir, c'est s'il y a d'autres considérations que j'ai pu ignorer. Pour le moment, je suis enclin à franchir le pas et à commencer à utiliser les GUID. Je ne suis en aucun cas un expert en bases de données, donc j'apprécierais vraiment tout conseil.

Barguast
la source
2
Comment généreriez-vous un "GUID séquentiel"?
C'est une implémentation personnalisée. Il s'agit essentiellement d'un format de type GUID qui a 6 octets remplacés par des octets d'horodatage et 2 octets qui représentent un numéro de séquence où l'horodatage est le même. Il n'est pas garanti de produire des valeurs séquentielles parfaites, mais il est assez bon pour que la fragmentation d'index ne soit pas un problème pour moi.
Chargez-vous donc ces données depuis plusieurs sources différentes? Je suppose également que l'index qui vous inquiète au sujet de la fragmentation est l'index clusterisé?
2
Si vous utilisez un GUID séquentiel, vous devriez regarder NEWSEQUENTIALID (). Il devrait faire ce que vous voulez (augmenter de façon monotone) et ne dépend pas de code personnalisé.
2
Regardez le post de Jeremiah Peschka sur Les problèmes avec les clés Bonne lecture et il a traité cette implémentation plusieurs fois.
billinkc

Réponses:

4

Je suis dans une même situation similaire. Actuellement, j'utilise l'approche GUID séquentielle et je n'ai pas de fragmentation et de génération de clé facile.

J'ai remarqué deux inconvénients qui m'ont amené à commencer la migration vers bigint:

  1. Utilisation de l'espace . 8 octets de plus par index. Multipliez cela par 10 index environ et vous obtenez un énorme gaspillage d'espace.
  2. Les index Columnstore ne prennent pas en charge les GUID.

(2) Était le tueur pour moi.

Je vais maintenant générer mes clés comme ceci:

yyMMddHH1234567890

Je vais utiliser une date principale plus une heure et avoir une partie séquentielle après cela. Cela me permet d'interroger par plage mes données par date sans aucun indice d'addition. C'est un bon bonus pour moi.

Je vais générer la partie séquentielle du bigint en utilisant un algorithme HiLo qui se prête bien à la distribution .

J'espère que certains de ces transferts à votre situation. Je recommande vraiment d'utiliser bigint.

usr
la source
1
Marquer cela comme la «réponse», car c'est la meilleure solution (et vous semblez apprécier ce que je demande et pourquoi ce n'est pas aussi simple qu'il y paraît). Je pense que je vais aller avec un générateur de séquence partagé (qui fonctionnera de manière similaire à votre suggestion d'algorithme HiLo). J'ai ce travail sur un autre système avec peu de problèmes, je vais juste devoir supporter la dépendance supplémentaire. Tant pis. Merci.
Barguast
3

Avec un type INT, à partir de 1, vous obtenez plus de 2 milliards de lignes possibles - ce qui devrait être plus que suffisant pour la grande majorité des cas. Avec BIGINT, vous obtenez environ 922 quadrillions (922 avec 15 zéros - 922'000 milliards) - assez pour vous ??

Si vous utilisez un INT IDENTITYdébut à 1 et que vous insérez une ligne toutes les secondes, vous avez besoin de 66,5 ans avant d'atteindre la limite de 2 milliards ...

Si vous utilisez un BIGINT IDENTITYdébut à 1 et que vous insérez mille lignes par seconde, vous avez besoin de 292 millions d'années époustouflantes avant d'atteindre la limite de 922 quadrillions ....

En utilisant vos 10 millions de lignes par jour, cela vous prendra suffisamment de chiffres pour environ 1'844'674'407'370 jours ( 1844 milliards de jours ou un tick sur 5 milliards d'années ) de données - est-ce suffisant pour vos besoins ?

En savoir plus (avec toutes les options disponibles) dans la documentation MSDN en ligne .

marc_s
la source
1
Le taux d'insertion de 10 millions de lignes par jour épuiserait la plage INT en 200 jours.
mceda
@mceda: oui - ai-je réclamé autre chose? Cela n'épuise pas la BIGINTgamme aussi rapidement, cependant ...
marc_s
Merci, mais comme je l'ai dit dans ma question, j'ai besoin des identifiants avant de les envoyer à la base de données. Les données sont relationnelles, j'ai donc besoin d'attribuer des clés primaires et étrangères avant de les copier en bloc. Sans cela, un IDENTITY BIGINT serait probablement parfait.
2
@Barguast: ne pourriez-vous pas simplement insérer vos données en bloc dans une table intermédiaire (sans l'identité), puis les déplacer à partir de là dans vos tables de données réelles à l'aide BIGINT IDENTITY?
marc_s
@marc_s: oui, le calcul fourni n'était pas aligné sur la question: "Si vous utilisez une INT IDENTITY commençant à 1 et que vous insérez une ligne toutes les secondes, vous avez besoin de 66,5 ans avant d'atteindre la limite de 2 milliards".
mceda
2

Je vous recommande d'utiliser SEQUENCE de type de données BIGINT dans SQL 2012.Ceci est beaucoup plus flexible que IDENTITY avec des options comme cache / nocache, vous pouvez également attribuer une plage de séquence pour votre opération par lots en tant que sp_sequence_get_range.


la source
Malheureusement, SEQUENCE n'est pas pris en charge sur Sql Azure.
Timothy Lee Russell
2

Est-ce la raison pour laquelle vous ne pouvez pas utiliser IDENTITY car il existe déjà des relations de clé étrangère entre des tables distinctes que vous chargez? Et il n'y a pas d'autre clé naturelle pour que vous puissiez les relier dans une opération d'une zone de rassemblement à la zone de production? Pour cette raison, j'aimerais en savoir un peu plus sur la façon dont ils sont actuellement "liés" dans le système source avant de les copier en bloc? Les systèmes sources multiples utilisent-ils simplement leurs propres séquences et ont-ils la possibilité de créer des séquences conflictuelles lorsqu'ils sont introduits dans une base de données partagée?

La technique COMB ID / GUID séquentiel est une technique que je connais, et elle est réalisable chaque fois que vous avez réellement besoin de cette unicité globale attribuée en dehors de la base de données - c'est en fait une identité de ligne utilisable à l'intérieur et à l'extérieur de la base de données. Pour cette raison, dans des environnements hautement distribués ou des scénarios déconnectés, c'est un choix OK

Sauf si vous n'en avez vraiment pas besoin, car cette différence de largeur supplémentaire est importante lorsque la taille des données augmente et que ces clés se trouvent dans chaque index et les ensembles de travail pour un grand nombre de requêtes.

De plus, avec la génération distribuée, si les lignes ne viennent pas dans l'ordre de la colonne GUID, les problèmes liés à l'utilisation de cette clé d'index en cluster (étroit, statique, croissant) peuvent potentiellement causer une certaine fragmentation par rapport au clustering sur une IDENTITY toujours rester.

Cade Roux
la source
0

En général, il est possible d'utiliser la OUTPUTclause de INSERTcommande pour insérer des données dans les deux tables et les associer au champ d'identité.

L'identifiant qui est basé sur l'horodatage ne doit pas être considéré comme fiable - il dépend de l'horloge système qui dépend à son tour de beaucoup de choses - de l'horloge matérielle aux services de synchronisation horaire.

Serg
la source