Différence de performances entre l'index cluster et l'index non cluster

22

Je lisais Clusteredet Non Clustered Indexes.

Clustered Index- Il contient des pages de données. Cela signifie que les informations de ligne complètes seront présentes dans la colonne d'index clusterisé.

Non Clustered Index- Il contient uniquement les informations du localisateur de lignes sous la forme d'une colonne d'index clusterisé (si disponible) ou de l'identificateur de fichier + numéro de page + nombre total de lignes dans une page. Cela signifie que le moteur de requête doit effectuer une étape supplémentaire afin de localiser les données réelles.

Requête - Comment puis-je vérifier la différence de performances à l'aide d'un exemple pratique car nous savons que la table ne peut en avoir qu'un Clustered Indexet fournit sortingau Clustered Index Columnet Non Clustered Indexne fournit pas sortinget peut prendre Non Clustered Indexesen charge 999 in SQL Server 2008et 249 in SQL Server 2005.


la source
2
Différence de performances lorsque vous faites quoi?, Quel type de travail vous voulez faire avec cette table?, Il n'y a pas une seule solution qui convient à tous les besoins
Lamak
2
Une discussion tangible ici peut-être. stackoverflow.com/questions/91688/… stackoverflow.com/questions/5070529/… stackoverflow.com/questions/1251636/… Nous pourrions rédiger une dissertation sur les différences entre les index cluster et non cluster, mais je ne pense pas que nous dirait tout ce qui n'est pas déjà disponible pour que vous puissiez le lire.
Aaron Bertrand
4
Vous avez écrit: "Cela signifie que le moteur de requête doit effectuer une étape supplémentaire pour localiser les données réelles." En fait, si vous n'avez besoin que de colonnes couvertes dans l'index , vous n'avez pas besoin de prendre d'autres mesures après avoir trouvé vos lignes cibles dans l'index non cluster. Ce n'est que lorsque vous avez besoin de colonnes non couvertes par l'index non cluster que SQL Server doit effectuer une recherche de signet .
Nick Chammas

Réponses:

43

Très bonne question car c'est un concept si important. C'est un gros sujet cependant et ce que je vais vous montrer est une simplification afin que vous puissiez comprendre les concepts de base.

Premièrement, lorsque vous voyez une table de réflexion d' index clusterisé . Dans SQL Server, si une table ne contient pas d'index cluster, il s'agit d'un segment. La création d'un index cluster sur la table transforme en fait la table en une structure de type b-tree. Votre index cluster EST votre table il n'est pas séparé de la table

Vous êtes-vous déjà demandé pourquoi vous ne pouvez avoir qu'un seul index cluster? Eh bien, si nous avions deux index clusterisés, nous aurions besoin de deux copies de la table. Il contient les données après tout.

Je vais essayer d'expliquer cela en utilisant un exemple simple.

REMARQUE: J'ai créé le tableau dans cet exemple et l'ai rempli avec plus de 3 millions d'entrées aléatoires. Ensuite, a exécuté les requêtes réelles et collé les plans d'exécution ici.

Ce que vous devez vraiment comprendre, c'est la notation O ou l' efficacité opérationnelle . Supposons que vous ayez le tableau suivant.

CREATE TABLE [dbo].[Customer](
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [varchar](100) NOT NULL,
[CustomerSurname] [varchar](100) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED 
(
[CustomerID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF
  , IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS  = ON
  , ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Nous avons donc ici une table de base avec une clé en cluster sur CustomerID (la clé primaire est en cluster par défaut). Ainsi, la table est organisée / ordonnée en fonction de la clé primaire CustomerID. Les niveaux intermédiaires contiendront les valeurs CustomerID. Les pages de données contiendront la ligne entière, c'est donc la ligne du tableau.

Nous allons également créer un index non clusterisé sur le champ CustomerName. Le code suivant le fera.

CREATE NONCLUSTERED INDEX [ix_Customer_CustomerName] ON [dbo].[Customer] 
 (
[CustomerName] ASC
 )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF
  , SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF
  , DROP_EXISTING = OFF, ONLINE = OFF
  , ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

Ainsi, dans cet index, vous trouverez sur les pages de données / nœuds de niveau feuille un pointeur vers les niveaux intermédiaires de l'index clusterisé. L'index est organisé / ordonné autour du champ CustomerName. Ainsi, le niveau intermédiaire contient les valeurs CustomerName et le niveau feuille contiendra le pointeur (ces valeurs de pointeur sont en fait les valeurs de clé primaire ou la colonne CustomerID).

Exactement si nous exécutons la requête suivante:

SELECT * FROM Customer WHERE CustomerID = 1 

SQL lira probablement l'index clusterisé via une opération de recherche. Une opération de recherche est une recherche binaire qui est beaucoup plus efficace qu'une analyse qui est une recherche séquentielle. Ainsi, dans notre exemple ci-dessus, l'index est lu et en utilisant une recherche binaire, SQL peut éliminer les données qui ne correspondent pas aux critères que nous recherchons. Voir la capture d'écran ci-jointe pour le plan de requête.

entrez la description de l'image ici

Ainsi, le nombre d'opérations ou de notation O pour l'opération de recherche est le suivant:

  1. Effectuez une recherche binaire sur un index clusterisé en comparant la valeur recherchée aux valeurs du niveau intermédiaire.
  2. Renvoyer les valeurs qui correspondent (rappelez-vous que puisque l'index cluster contient toutes les données, il peut renvoyer toutes les colonnes de l'index car ce sont les données de ligne)

Il s'agit donc de deux opérations. Cependant, si nous avons exécuté la requête suivante:

SELECT * FROM Customer WHERE CustomerName ='John'

SQL va maintenant utiliser l'index non clusterisé sur le CustomerName pour effectuer la recherche. Cependant, puisqu'il s'agit d'un index non clusterisé, il ne contient pas toutes les données de la ligne.

Ainsi, SQL effectuera la recherche aux niveaux intermédiaires pour trouver les enregistrements qui correspondent, puis effectuera une recherche en utilisant les valeurs renvoyées pour effectuer une autre recherche sur l'index cluster (alias la table) pour récupérer les données réelles. Cela semble déroutant, je sais, mais lisez la suite et tout deviendra clair.

Étant donné que notre index non clusterisé ne contient que le champ CustomerName (les valeurs de champ indexées stockées dans les nœuds intermédiaires) et le pointeur vers les données qui sont le CustomerID, l'index n'a pas d'enregistrement de CustomerSurname. Le CustomerSurname doit être récupéré à partir de l'index ou de la table en cluster.

Lors de l'exécution de cette requête, j'obtiens le plan d'exécution suivant:

entrez la description de l'image ici

Il y a deux choses importantes à noter dans la capture d'écran ci-dessus

  1. SQL dit que j'ai un index manquant (le texte en vert). SQL suggère que je crée un index sur CustomerName qui inclut CustomerID et CustomerSurname.
  2. Vous verrez également que 99% du temps de la requête est consacré à la recherche de clé sur l'index de clé primaire / l'index clusterisé.

Pourquoi SQL suggère-t-il à nouveau l'index sur CustomerName? Eh bien, puisque l'index contient uniquement le CustomerID et le CustomerName SQL doit toujours trouver le CustomerSurname à partir des index de table / cluster.

Si nous avons créé l'index et que nous avons inclus la colonne CustomerSurname dans l'index, SQL pourrait satisfaire l'intégralité de la requête en lisant simplement l'index non cluster. C'est pourquoi SQL suggère que je modifie mon index non clusterisé.

Ici, vous pouvez voir l'opération supplémentaire que SQL doit faire pour obtenir la colonne CustomerSurname à partir de la clé en cluster

Ainsi, le nombre d'opérations est le suivant:

  1. Effectuer une recherche binaire sur un index non clusterisé en comparant la valeur recherchée aux valeurs du niveau intermédiaire
  2. Pour les nœuds qui correspondent, lisez le nœud de niveau feuille qui contiendra le pointeur pour les données dans l'index clusterisé (les nœuds de niveau feuille contiendront d'ailleurs les valeurs de clé primaire).
  3. Pour chaque valeur retournée, effectuez une lecture sur l'index cluster (la table) pour obtenir les valeurs de ligne ici, nous lirions le CustomerSurname.
  4. Renvoyer les lignes correspondantes

Il s'agit de 4 opérations pour extraire les valeurs. Deux fois le nombre d'opérations nécessaires par rapport à la lecture de l'index clusterisé. Ils vous montrent que votre index cluster est votre index le plus puissant car il contient toutes les données.

Juste pour clarifier un dernier point. Pourquoi dois-je dire que le pointeur dans l'index non cluster est la valeur de clé primaire? Eh bien pour démontrer que les nœuds de niveau feuille de l'index non clusterisé contiennent la valeur de clé primaire, je change ma requête en:

SELECT CustomerID
FROM Customer
WHERE CustomerName='Jane'

Dans cette requête, SQL peut lire le CustomerID à partir de l'index non cluster. Il n'a pas besoin d'effectuer une recherche sur l'index clusterisé. Vous pouvez le voir sur le plan d'exécution qui ressemble à ceci.

entrez la description de l'image ici

Notez la différence entre cette requête et la requête précédente. Il n'y a pas de recherche. SQL peut trouver toutes les données dans l'index non clusterisé

J'espère que vous pourrez commencer à comprendre que l'index cluster est la table et que les index non cluster NE contiennent PAS toutes les données. L'indexation accélérera les sélections car les recherches binaires peuvent être effectuées mais seuls les index clusterisés contiennent toutes les données. Ainsi, une recherche sur un index non clusterisé entraînera presque toujours le chargement de valeurs à partir de l'index clusterisé. Ces opérations supplémentaires rendent les index non cluster moins efficaces qu'un index cluster.

Espérons que cela arrange les choses. Si quelque chose n'a pas de sens, veuillez poster un commentaire et je vais essayer de clarifier. Il est assez tard ici et mon cerveau se sent un peu plat. Il est temps pour un taureau rouge.

Namphibian
la source
J'ai une question. POURQUOI est la recherche qu'un index recherche sur l'index non clusterisé sur CustomerName pour cette requête SELECT * FROM Customer WHERE CustomerName = 'John'. Comme il s'agit d'un index non clusterisé, le nom personnalisé ne sera pas trié. Donc, une analyse d'index ne devrait pas être effectuée.
ckv
BTW Grande réponse totalement comprise sauf la question ci-dessus.
ckv
1
Un index est trié dans l'ordre des données. Par exemple, il serait trié sur le nom du client car il s'agit de la valeur indexée. Il est donc trié. N'oubliez pas qu'il doit encore scanner le niveau ou les pages des feuilles.
Namphibian le
9

"Cela signifie que le moteur de requête doit effectuer une étape supplémentaire afin de localiser les données réelles."

Pas nécessairement - si l'index couvre une requête donnée, aucun déplacement ne doit être effectué vers les pages de données. De plus, avec les colonnes incluses, des colonnes supplémentaires peuvent être ajoutées à un index non clusterisé pour le rendre couvrant sans modifier la taille de la clé.

Donc, la réponse ultime est - cela dépend (sur beaucoup plus d'informations que vous ne pouvez vraiment couvrir dans une seule question) - vous devez comprendre toutes les capacités des index et le plan d'exécution pour une requête donnée peut différer de vos attentes.

Une règle générale que j'ai est qu'une table a toujours un index cluster (et généralement sur une identité ou un GUID séquentiel), mais des index non cluster sont ajoutés pour la performance. Mais il y a toujours des exceptions - les tables de tas ont une place, les index cluster plus larges ont une place. Les index apparemment redondants qui sont plus étroits pour contenir plus de lignes par page ont une place. etc.

Et je ne m'inquiéterais pas des limites des différents indices autorisés - cela ne va certainement pas entrer en jeu dans de nombreux exemples réels.

Cade Roux
la source
2
+1 pour there are always exceptions- trop de gens omettent cela et pensent que chaque index cluster devrait être int identityn'importe quoi.
JNK