Sélection d'index en cluster - PK ou FK?

11

J'ai une table SQL Server 2014 qui ressemble à ceci:

OrderId     int           not null IDENTITY --this is the primary key column
OrderDate   datetime2     not null
CustomerId  int           not null
Description nvarchar(255) null

Certaines personnes de mon équipe ont suggéré que l'index clusterisé soit activé OrderId, mais je pense que le CustomerId+ OrderIdserait un meilleur choix pour les raisons suivantes:

  • Presque toutes les requêtes seront recherchées WHERE CustomerId = @param, pasOrderId
  • CustomerIdest une clé étrangère de la Customertable, donc avoir un index cluster avec CustomerIddevrait accélérer les jointures
  • Bien qu'il CustomerIdne soit pas unique, avoir la OrderIdcolonne supplémentaire spécifiée dans l'index garantira l'unicité (nous pouvons utiliser le UNIQUEmot - clé lors de la création de l'index cluster sur ces 2 colonnes, pour éviter le surcoût de ne pas avoir d'unicité)
  • Une fois les données insérées, le CustomerIdet OrderIdne change jamais, donc ces lignes ne se déplaceraient pas après l'écriture initiale.
  • L'accès aux données se fait via un ORM qui demande toutes les colonnes par défaut, donc quand une requête basée sur CustomerIdarrive, l'index clusterisé pourra fournir toutes les colonnes sans aucun travail supplémentaire.

L' approche CustomerIdet OrderIdsemble-t-elle la meilleure option compte tenu de ce qui précède? Ou, est-il OrderIdmeilleur en soi, car il s'agit d'une seule colonne qui garantit l'unicité en soi?

Actuellement, la table a un index clusterisé OrderIdet un index non clusterisé CustomerId, mais il ne couvre pas, donc puisque nous utilisons un ORM et que toutes les colonnes sont demandées, il est supplémentaire de les récupérer. Donc, avec ce post, j'essaie d'envisager d'améliorer les performances avec un meilleur CI.

L'activité sur notre base de données est d'environ 85% de lectures et 15% d'écritures.

Andy
la source

Réponses:

5

Réponse du wiki communautaire :

Je pense qu'une clé d'index composite en cluster avec CustomerID comme première colonne sera la meilleure car c'est dans la WHEREclause de presque toutes les requêtes.

Il peut y avoir plus de divisions par rapport à une clé incrémentielle (ou plus probablement une densité de page sous-optimale pendant un certain temps si vous gérez et maintenez le facteur de remplissage pour éviter les «mauvaises» divisions). Cependant, l'amélioration globale des performances pour les requêtes des clients est substantielle, car la recherche de clé est évitée.

OrderID ou OrderDate peut être préférable pour la deuxième colonne en fonction de vos requêtes les plus critiques.

Par exemple, si les clients voient une liste chronologique des commandes récentes après s'être connecté à un site Web, OrderDate devrait être le suivant, pour optimiser ORDER BY OrderDate DESC.

Si vous choisissez OrderID comme index clusterisé, avec un index non clusterisé sur CustomerID , vous obtiendrez toujours les divisions et la fragmentation, uniquement dans l'index non clusterisé.

utilisateur126897
la source
3

Si ce tableau est très intensif en écriture (par exemple, de nombreuses autres INSERTdéclarations se produisent plutôt que des SELECTdéclarations contre lui), je vais être en désaccord avec la réponse du wiki .

Le choix de CustomerID comme première colonne d'une clé en cluster composite va générer de nombreux fractionnements de mi-page . Nous espérons que vous avez beaucoup de clients existants et que vous obtenez également de nombreux nouveaux clients tout le temps. Parce que les clients passent (espérons-le) plusieurs commandes à mesure que votre entreprise continue de croître, cette approche présentera une bonne quantité de fractionnements de mi-page qui vont tuer les performances non seulement sur les écritures, mais aussi sur les lectures car vos index seront tous deux fortement fragmentés et contiennent probablement des quantités plus importantes d'espace blanc (ce qui signifie un stockage et une mémoire gaspillés).

Si vous pensez que CustomerID doit être une colonne de tête d'un index cluster composé, vous pouvez réduire l'impact des séparations de mi-page en ajustant FILLFACTORtous les index de ce tableau. Cela réduira le nombre de fractionnements de mi-page en augmentant la taille de la table / de l'index. Si vous souhaitez emprunter cette voie, je vous suggère de tester avec une valeur de 80 et de réduire si l' analyse révèle que les fractionnements de mi-page continuent de nuire aux performances.

Ma suggestion est d'utiliser OrderId. OrderID doit naturellement être séquentiel et générer davantage de fractionnements de fin de page qui sont bons et attendus avec la croissance de la table. De plus, cette approche fonctionnera mieux avec le partitionnement de table si vous choisissez d'utiliser la colonne OrderDate comme clé de partition. En ce qui concerne les requêtes qui utilisent constamment le champ CustomerID, créez un index non cluster pour gérer ces requêtes. Cet index devrait être défini avec le bon FILLFACTORcar il souffrira des divisions de mi-page que je mentionne ci-dessus, bien que celles-ci ne soient pas aussi mauvaises dans l'ensemble contrairement à si les divisions se produisaient par rapport à l'index clusterisé.

L'activité sur notre base de données est d'environ 85% de lectures et 15% d'écritures.

CustomerID+ OrderID(et en spécifiant un facteur de remplissage pour permettre une croissance sans fractionnement) est probablement mieux si cette évaluation est vraie. Assurez - vous simplement que l'évaluation est exacte. Test test test.

John Eisbrener
la source
1
Notez que l'insertion d'une commande pour le dernier (ou le seul) client sur une page n'est pas un "fractionnement de mi-page". Ainsi, si les commandes par client sont élevées ou si la largeur des lignes est importante, moins d'insertions de commande nécessiteront des «fractionnements de mi-page».
David Browne - Microsoft