web-dev-qa-db-fra.com

Indexation d'un PK GUID dans SQL Server 2012

Mes développeurs ont configuré leur application pour utiliser les GUID en tant que PK pour presque toutes leurs tables et par défaut, SQL Server a configuré l'index cluster sur ces PK.

Le système est relativement jeune et nos plus grandes tables ne dépassent pas un million de lignes, mais nous examinons notre indexation et voulons pouvoir évoluer rapidement car cela pourrait être nécessaire dans un proche avenir.

Donc, ma première inclinaison a été de déplacer l'index clusterisé vers le champ créé, qui est une représentation bigint d'un DateTime. Cependant, la seule façon dont je peux rendre le CX unique serait d'inclure la colonne GUID dans ce CX mais l'ordre en le créant d'abord.

Cela rendrait-il la clé de clustering trop large et augmenterait-il les performances d'écriture? Les lectures sont également importantes, mais les écritures sont probablement une plus grande préoccupation à ce stade.

13
njkroes

Les principaux problèmes avec les GUID, en particulier ceux non séquentiels, sont:

  • Taille de la clé (16 octets contre 4 octets pour un INT): cela signifie que vous stockez 4 fois la quantité de données dans votre clé avec cet espace supplémentaire pour tous les index s'il s'agit de votre index cluster.
  • Fragmentation d'index: il est pratiquement impossible de conserver une colonne non séquentielle GUID défragmentée en raison de la nature complètement aléatoire des valeurs de clé.

Alors qu'est-ce que cela signifie pour votre situation? Cela dépend de votre conception. Si votre système concerne simplement les écritures et que vous ne vous souciez pas de la récupération des données, l'approche décrite par Thomas K est exacte. Cependant, vous devez garder à l'esprit qu'en poursuivant cette stratégie, vous créez de nombreux problèmes potentiels pour la lecture de ces données et leur stockage. Comme Jon Seigel le fait remarquer, vous occuperez également plus d'espace et aurez essentiellement un gonflement de la mémoire.

La principale question concernant les GUID est de savoir à quel point ils sont nécessaires. Les développeurs les aiment parce qu'ils garantissent l'unicité globale, mais c'est une occasion rare que ce type d'unicité soit nécessaire. Mais considérez que si votre nombre maximal de valeurs est inférieur à 2 147 483 647 (la valeur maximale d'un entier signé de 4 octets), vous n'utilisez probablement pas le type de données approprié pour votre clé. Même en utilisant BIGINT (8 octets), votre valeur maximale est de 9 223 372 036 854 775 807. Cela est généralement suffisant pour toute base de données non globale (et de nombreuses bases de données globales) si vous avez besoin d'une valeur d'incrémentation automatique pour une clé unique.

Enfin, en ce qui concerne l'utilisation d'un segment de mémoire par rapport à un index clusterisé, si vous écrivez uniquement des données, un segment de mémoire serait plus efficace, car vous minimisez la surcharge pour les insertions. Cependant, les tas dans SQL Server sont extrêmement inefficaces pour la récupération de données. D'après mon expérience, un index cluster est toujours souhaitable si vous avez la possibilité d'en déclarer un. J'ai vu l'ajout d'un index clusterisé à une table (4 milliards + d'enregistrements) améliorer les performances de sélection globales d'un facteur 6.

Information additionnelle:

20
Mike Fal

Il n'y a rien de mal à GUID comme clés et clusters dans un système OLTP (sauf si vous avez BEAUCOUP d'index sur la table qui souffrent de l'augmentation de la taille de En fait, ils sont beaucoup plus évolutifs que les colonnes IDENTITY.

Il y a une croyance répandue que GUID sont un grand problème dans SQL Server - en grande partie, c'est tout simplement faux. En fait, GUID peut être nettement plus évolutif sur les boîtiers de plus de 8 cœurs environ:

Je suis désolé, mais vos développeurs ont raison. Souciez-vous d'autres choses avant de vous soucier du GUID.

Oh, et enfin: pourquoi voulez-vous un index de cluster en premier lieu? Si votre problème est un système OLTP avec beaucoup de petits index, vous êtes probablement mieux avec un tas.

Examinons maintenant ce que la fragmentation (que le GUID introduira) fait à vos lectures. Il y a trois problèmes majeurs avec la fragmentation:

  1. La page divise les E/S du disque de coût
  2. Les demi-pages pleines ne sont pas aussi efficaces en mémoire que les pages complètes
  3. Cela entraîne le stockage des pages dans le désordre, ce qui rend les E/S séquentielles moins probables

Étant donné que votre préoccupation dans la question concerne l'évolutivité, que nous pouvons définir comme "l'ajout de matériel accélère le système", ce sont les moindres problèmes. Pour aborder chacun à son tour

Annonce 1) Si vous voulez évoluer, vous pouvez vous permettre d'acheter des E/S. Même un SSD Samsung/Intel 512 Go bon marché (à quelques USD/Go) vous permettra de dépasser les 100 000 IOPS. Vous ne consommerez pas cela de sitôt sur un système à 2 prises. Et si vous rencontrez cela, achetez-en un de plus et vous êtes prêt

Annonce 2) Si vous supprimez votre tableau, vous aurez quand même des pages à moitié pleines. Et même si vous ne le faites pas, la mémoire est bon marché et pour tous sauf les plus grands systèmes OLTP - les données chaudes devraient y tenir. La recherche de plus de données dans des pages sous-optimise lorsque vous êtes à la recherche d'échelle.

Annonce 3) Un tableau construit à partir de données fréquemment fractionnées et très fragmentées effectue des E/S aléatoires exactement à la même vitesse qu'un tableau rempli séquentiellement

En ce qui concerne la jointure, il existe deux principaux types de jointures que vous êtes susceptible de voir dans une charge de travail de type OLTP: hachage et boucle. Regardons chacun à son tour:

Jointure par hachage: Une jointure par hachage suppose que la petite table est analysée et que la plus grande est généralement recherchée. Les petites tables sont très probablement en mémoire, donc les E/S ne sont pas votre problème ici. Nous avons déjà évoqué le fait que les recherches ont le même coût dans un indice fragmenté que dans un indice non fragmenté

Jointure de boucle: La table externe sera recherchée. Même coût

Vous pouvez également avoir beaucoup de mauvaises analyses de table en cours - mais alors GUID n'est pas encore votre problème, une bonne indexation l'est.

Maintenant, vous pouvez avoir des analyses de plage légitimes en cours (en particulier lors de la connexion sur des clés étrangères) et dans ce cas, les données fragmentées sont moins "compressées" par rapport aux données non fragmentées. Mais considérons les jointures que vous verrez probablement dans des données 3NF bien indexées:

  1. Une jointure d'une table qui a une référence de clé étrangère à la clé primaire de la table qu'elle référence

  2. L'inverse

Annonce 1) Dans ce cas, vous allez pour une seule recherche à la clé primaire - joindre n à 1. Fragmentation ou non, même coût (une recherche)

Annonce 2) Dans ce cas, vous vous joignez à la même clé, mais vous pouvez récupérer plusieurs lignes (recherche de plage). La jointure dans ce cas est de 1 à n. Cependant, la table étrangère que vous recherchez, vous recherchez la même clé, qui est tout aussi susceptible d'être sur la même page dans un index fragmenté que sur une index non fragmentée.

Considérez ces clés étrangères pendant un moment. Même si vous aviez "parfaitement" séquentiellement posé nos clés primaires - tout ce qui pointe vers cette clé sera toujours non séquentiel.

Bien sûr, vous pouvez exécuter sur une machine virtuelle dans certains SAN dans une banque qui est bon marché en argent et en processus élevé. Ensuite, tous ces conseils seront perdus. Mais si tel est votre monde , l'évolutivité n'est probablement pas ce que vous recherchez - vous recherchez des performances et une vitesse/coût élevés - qui sont deux choses différentes.

13
Thomas Kejser

Thomas: certains de vos points sont parfaitement logiques et je suis d'accord avec eux tous. Si vous utilisez des disques SSD, l'équilibre de ce que vous optimisez change. Random vs séquentiel n'est pas la même discussion que le disque en rotation.

Je suis particulièrement d'accord que prendre une vue DB pure est horriblement mauvais. Rendre votre application lente et non évolutive pour améliorer juste les performances de la base de données peuvent être assez peu judicieuses.

Le gros problème avec IDENTITY (ou séquence, ou tout généré dans la base de données) est qu'il est horriblement lent car il nécessite un aller-retour vers la base de données pour créer une clé, ce qui crée automatiquement un goulot d'étranglement dans votre base de données, cela oblige les applications à effectuer un appel de base de données pour commencer à utiliser une clé. La création d'un GUID résout ce problème en utilisant l'application pour créer la clé, il est garanti d'être globalement unique (par définition), et les couches d'application peuvent donc l'utiliser pour transmettre l'enregistrement AVANT d'engager un Aller-retour DB.

Mais j'ai tendance à utiliser une alternative aux GUID. Ma préférence personnelle pour un type de données ici est un BIGINT unique au monde, généré par l'application. Comment procéder? Dans l'exemple le plus trivial, vous ajoutez une petite fonction TRÈS légère à votre application pour hacher un GUID. En supposant que votre fonction de hachage est rapide et relativement rapide (voir CityHash de Google pour un exemple: http://google-opensource.blogspot.in/2011/04/introducing-cityhash.html - assurez-vous de obtenir toutes les étapes de compilation correctement, ou la variante FNV1a de http://tools.ietf.org/html/draft-eastlake-fnv- pour le code simple) cela vous permet de bénéficier des deux applications généré des identifiants uniques et une valeur de clé 64 bits avec laquelle les processeurs fonctionnent mieux.

Il existe d'autres façons de générer des BIGINT, et dans ces deux algues, il existe un risque de collision de hachage - lisez et prenez des décisions conscientes.

5
Mark Stacey