2010-04-30 7 views
2

J'ai une table qui stocke des millions de lignes. Il ressemble à ceci:Dois-je créer un index cluster unique ou un index cluster non unique sur cette table SQL 2005?

Table_Docs 
ID, Bigint (Identity col) 
OutputFileID, int 
Sequence, int 
…(many other fields) 

Nous nous trouvons dans une situation où le développeur qui a conçu le fait OutputFileID l'index cluster. Ce n'est pas unique. Il peut y avoir des milliers d'enregistrements avec cet ID. Il n'a aucun avantage pour les processus utilisant cette table, donc nous prévoyons de l'enlever.

La question, est ce que pour le changer à ... J'ai deux candidats, la colonne identité ID est un choix naturel. Cependant, nous avons un processus qui fait beaucoup de commandes de mise à jour sur cette table, et il utilise la séquence pour le faire. La séquence est non unique. La plupart des enregistrements n'en contiennent qu'un, mais environ 20% peuvent avoir deux ou plusieurs enregistrements avec la même séquence.

L'application INSERT est une pièce VB6 qui lance des milliers d'instructions d'insertion à la table. Les valeurs insérées ne sont jamais dans un ordre particulier. Ainsi, la séquence d'un insert peut être 12345, et la suivante pourrait être 12245. Je sais que cela pourrait amener SQL à déplacer beaucoup de données pour garder l'index cluster dans l'ordre. Cependant, la séquence des inserts est généralement proche d'être en ordre. Toutes les insertions auraient lieu à la fin de la table en cluster. Ex: J'ai 5 millions de disques avec une séquence de 1 à 5 millions. L'application INSERT insérera des séquences à la fin de cette plage à tout moment. La réorganisation des données devrait être minimale (des dizaines de milliers d'enregistrements au maximum).

Maintenant, l'application UPDATE est notre étoile .NET. Il effectue toutes les mises à jour dans la colonne Séquence. “Update Table_Docs Set Feild1=This, Field2=That…WHERE Sequence =12345” - des centaines de milliers d'entre eux par jour. Les mises à jour sont complètement et totalement, au hasard, toucher tous les points de la table.

Tous les autres processus font simplement des SELECT sur ce (pages Web). Les index réguliers couvrent ceux-ci. Donc, ma question est, quoi de mieux ... .un index cluster unique sur la colonne ID, bénéficiant de l'application INSERT, ou un index cluster non unique sur la séquence, bénéficiant de l'application UPDATE?

Répondre

4

Tout d'abord, je voudrais certainement recommander d'avoir un index en cluster!

En second lieu, l'index cluster should be:

  • étroite
  • statique (jamais ou changer presque jamais)
  • uniques
  • toujours plus

donc d'une identité INT est un choix très bien pensé. Lorsque votre clé de clustering n'est pas unique, SQL Server ajoute un code d'identification de 4 octets à ces valeurs de colonne, ce qui rend votre clé de cluster et tous les index non clusterisés de cette table plus grands et moins optimaux.

Donc dans votre cas, je choisirais l'ID - il est étroit, statique, unique et en constante augmentation - ne peut pas être plus optimal que cela! Puisque le Sequence est fortement utilisé dans les instructions UPDATE, mettez certainement un index non-clusterisé dessus!

Voir l'excellent blog posts on choosing the right clustering key de Kimberly Tripp pour de l'information générale sur le sujet.

+1

Bonne réponse. J'ai vu la bannière disant qu'une réponse avait été ajoutée au moment où je me présentais. – TimothyAWiseman

+0

Merci. Je vais lire ce lien ce week-end. Je veux prendre une décision éclairée. Je devrais indiquer que mon index groupé de «Sequence» serait: • étroit (c'est un int) • statique (ne change jamais) • presque unique (très limité duplique 10-20% de tous les enregistrements au maximum et limité à moins de 5 lignes pour chaque doublon) • toujours croissant Est-ce une exception à la règle, compte tenu de ce que nous faisons? – Bremer

+0

@Bremer: si votre clé de cluster n'augmente pas, vous devrez gérer les divisions de page lorsque vous insérerez une nouvelle ligne au milieu d'une page entière -> pas si bon pour la performance. –

2

En règle générale, vous souhaitez que votre index clusterisé soit unique. Si ce n'est pas le cas, SQL Server ajoutera en fait un "uniquificateur" caché pour le forcer à être unique, et cela ajoute un surcoût. Donc, vous êtes probablement le meilleur en utilisant la colonne ID en tant qu'index. En guise de remarque, l'utilisation d'une colonne d'identité comme clé primaire est généralement appelée clé de substitution car elle n'est pas inhérente à vos données. Lorsque vous avez une clé naturelle unique disponible, c'est probablement un meilleur choix. Dans ce cas, il semble que vous ne le fassiez pas, l'utilisation de la clé de substitution unique est donc logique.

+0

Je sais que c'est la recommandation générale, mais je vois un cas unique ici. Ce scénario est-il probablement l'un des onces où la «règle générale» ne s'applique pas? – Bremer

+0

Je ne vois pas pourquoi ce cas est unique. Quant à savoir si la règle générale s'applique, d'après ce qui a été décrit jusqu'ici, je dirais que oui. Vraiment déterminer cela en détail nécessiterait des tests approfondis avec votre application exacte, tout cela. Mais tous les détails jusqu'à présent indiqueraient que la colonne d'identification est la voie à suivre. – TimothyAWiseman

1

La pire chose à propos des insertions hors service est la division des pages.

Lorsque SQL Server doit insérer un nouvel enregistrement dans une page d'index existante et n'y trouve aucune place, il prend la moitié des enregistrements de la page et les déplace dans un nouveau.

Dites, vous avez ces enregistrements remplissant la page entière:

1 2 3 4 5 6 7 8 9 

et ont besoin d'insérer un 10. Dans ce cas, SQL Server va juste commencer la nouvelle page.

Cependant, si vous avez ceci:

1 2 3 4 5 6 7 8 11 

, 10 devrait aller avant 11. Dans ce cas, SQL Server se déplacera dossiers 6-11 dans la nouvelle page:

6 7 8 9 10 11 

L'ancienne page, car il peut être facilement vu, restera la moitié remplie (uniquement les enregistrements 1-6 va aller là-bas qui sont très).

Cela augmentera la taille de l'index.

Créons deux exemples de tableaux:

CREATE TABLE perfect (id INT NOT NULL PRIMARY KEY, stuffing VARCHAR(300)) 
CREATE TABLE almost_perfect (id INT NOT NULL PRIMARY KEY, stuffing VARCHAR(300)) 

; 
WITH q(num) AS 
     (
     SELECT 1 
     UNION ALL 
     SELECT num + 1 
     FROM q 
     WHERE num < 200000 
     ) 
INSERT 
INTO perfect 
SELECT num, REPLICATE('*', 300) 
FROM q 
OPTION (MAXRECURSION 0) 

; 
WITH q(num) AS 
     (
     SELECT 1 
     UNION ALL 
     SELECT num + 1 
     FROM q 
     WHERE num < 200000 
     ) 
INSERT 
INTO almost_perfect 
SELECT num + CASE num % 5 WHEN 0 THEN 2 WHEN 1 THEN 0 ELSE 1 END, REPLICATE('*', 300) 
FROM q 
OPTION (MAXRECURSION 0) 

EXEC sp_spaceused N'perfect' 
EXEC sp_spaceused N'almost_perfect' 

perfect   200000 66960 KB 66672 KB 264 KB 24 KB 
almost_perfect 200000 128528 KB 128000 KB 496 KB 32 KB 

Même avec seulement 20% probabilité des enregistrements étant hors d'usage, la table devient deux fois plus grand. D'autre part, avoir une clé groupée sur Sequence réduira le I/O deux fois (car cela peut être fait avec une seule recherche d'index en cluster plutôt que deux non clus). Donc, je voudrais prendre un exemple de sous-ensemble de vos données, insérez-le dans la table de test avec un index en cluster sur Sequence et mesurer la taille de la table qui en résulte.

Si moins de deux fois la taille de la même table avec un index sur ID, je voudrais aller à l'index cluster sur Sequence (puisque le total résultant I/O sera moins).

Si vous décidez de créer un index cluster sur Sequence, faire ID un unclustered PRIMARY KEY et rendre l'index ordonné en clusters UNIQUE sur Sequence, ID.Cela va utiliser un ID significatif au lieu d'uniquiqueur opaque.

Questions connexes