En regardant un plan d'exécution d'une requête à exécution lente et j'ai remarqué que certains des nœuds sont la recherche d'index et certains d'entre eux sont l'analyse d'index.
Quelle est la différence entre la recherche d'index et une analyse d'index?
Lequel fonctionne mieux?
Comment SQL choisit-il l'un plutôt que l'autre?
Je me rends compte qu'il s'agit de 3 questions mais je pense que répondre à la première expliquera les autres.
Version courte: chercher c'est bien mieux
Version moins courte: la recherche est généralement bien meilleure, mais un grand nombre de recherches (causées par une mauvaise conception de requête avec des sous-requêtes corrélées désagréables par exemple, ou parce que vous effectuez de nombreuses requêtes dans une opération de curseur ou une autre boucle) peuvent être pires qu'une analyse, surtout si votre requête peut finir par renvoyer des données de la plupart des lignes de la table affectée.
Il permet de couvrir toute la famille pour les opérations de recherche de données afin de bien comprendre les implications en termes de performances.
Analyses de table: En l'absence d'index pertinents pour votre requête, le planificateur est obligé d'utiliser une analyse de table, ce qui signifie que chaque ligne est examinée. Cela peut entraîner la lecture de chaque page relative aux données de la table à partir du disque, ce qui est souvent le pire des cas. Notez que pour certaines requêtes, il utilisera une analyse de table même lorsqu'un index utile est présent - cela est généralement dû au fait que les données dans la table sont si petites qu'il est plus compliqué de parcourir les index (si tel est le cas, vous vous attendez à ce que le prévoir de changer à mesure que les données augmentent, en supposant que la mesure de sélectivité de l'indice est bonne).
Analyses d'index avec recherche de lignes: Aucun index pouvant être directement utilisé pour une recherche n'est trouvé mais un index contenant les bonnes colonnes est présent, un index peut être utilisé. Par exemple, si vous avez une grande table avec 20 colonnes avec un index sur column1, col2, col3 et que vous émettez SELECT col4 FROM exampletable WHERE col2=616
, Dans ce cas, l'analyse de l'index pour interroger col2
Est meilleure que l'analyse de la table entière. Une fois les lignes correspondantes trouvées, les pages de données doivent être lues pour récupérer col4 pour la sortie (ou une jointure supplémentaire), ce qui correspond à l'étape de "recherche de signet" lorsque vous la voyez dans les plans de requête.
Analyses d'index sans recherche de lignes: Si l'exemple ci-dessus était SELECT col1, col2, col3 FROM exampletable WHERE col2=616
, L'effort supplémentaire pour lire les pages de données n'est pas nécessaire: une fois que les lignes d'index correspondant à col2=616
Sont trouvées toutes les les données demandées sont connues. C'est pourquoi vous voyez parfois des colonnes qui ne seront jamais recherchées, mais qui seront probablement demandées pour la sortie, ajoutées à la fin des index - cela peut sauver les recherches de lignes. Lorsque vous ajoutez des colonnes à un index pour cette raison et pour cette raison uniquement, ajoutez-les avec la clause INCLUDE
pour indiquer au moteur qu'il n'a pas besoin d'optimiser la disposition d'index pour les requêtes basées sur ces colonnes (cela peut accélérer mises à jour apportées à ces colonnes). Les analyses d'index peuvent également résulter de requêtes sans clauses de filtrage: SELECT col2 FROM exampletable
Analysera cet exemple d'index au lieu des pages de table.
Recherche d'index (avec ou sans recherche de ligne) : Lors d'une recherche, tout l'index n'est pas pris en compte. Pour la requête SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567
, Le moteur de recherche peut trouver la première ligne qui correspondra en effectuant une recherche arborescente sur l'index sur c1
, Puis il pourra parcourir l'index dans l'ordre jusqu'à ce qu'il atteigne le fin de la plage (c'est la même chose avec une requête pour c1=1234
car il peut y avoir plusieurs lignes correspondant à la condition même pour une opération =
). Cela signifie que seules les pages d'index pertinentes (plus quelques-unes nécessaires pour la recherche initiale) doivent être lues à la place de chaque page de l'index (ou du tableau).
Clustered Indexes: Avec un index clusterisé, les données de la table sont stockées dans les nœuds terminaux de cet index au lieu d'être dans une structure de tas distincte. Cela signifie qu'il ne sera jamais nécessaire d'effectuer des recherches de lignes supplémentaires après avoir trouvé des lignes à l'aide de cet index, quelles que soient les colonnes nécessaires [sauf si vous avez des données hors page telles que des colonnes TEXT
ou des colonnes VARCHAR(MAX)
contenant des données longues].
Vous ne pouvez avoir qu'un seul index cluster pour cette raison[1], l'index cluster est votre table au lieu d'avoir une structure de tas distincte, donc si vous en utilisez une[2] choisissez où vous le mettez soigneusement afin d'obtenir un gain maximum.
Notez également que l'index clusterisé car la "clé de clustering" de la table est incluse dans chaque index non clusterisé de la table, donc un index clusterisé large n'est généralement pas une bonne idée.
[1] En fait, vous pouvez avoir effectivement plusieurs index clusterisés en définissant des index non clusterisés qui couvrent ou incluent chaque sur la table, mais cela risque de gaspiller de l'espace a un impact sur les performances d'écriture, donc si vous envisagez de le faire, assurez-vous que vous en avez vraiment besoin.
[2] Quand je dis "si vous utilisez un index clusterisé", notez qu'il est généralement recommandé que vous en ayez un sur chaque table. Il existe des exceptions, comme avec toutes les règles empiriques, les tables qui ne voient guère que les insertions en masse et les lectures non ordonnées (tables de transfert pour les processus ETL peut-être) étant l'exemple de compteur le plus courant.
Point supplémentaire: Scans incomplets:
Il est important de se rappeler que, selon le reste de la requête, une analyse de table/index peut ne pas réellement analyser toute la table - si la logique le permet, le plan de requête peut être en mesure de l'abandonner tôt. L'exemple le plus simple est SELECT TOP(1) * FROM HugeTable
- si vous regardez le plan de requête pour cela, vous verrez qu'une seule ligne a été renvoyée par l'analyse et si vous regardez le IO = statistiques (SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable
) vous verrez qu'il ne lit qu'un très petit nombre de pages (peut-être une seule).
La même chose peut se produire si le prédicat d'une clause WHERE
ou JOIN ... ON
Peut être exécuté simultanément avec l'analyse qui est la source de ses données. Le planificateur/exécuteur de requêtes peut parfois être très intelligent pour repousser les prédicats vers les sources de données pour permettre la fin précoce des analyses de cette manière (et parfois vous pouvez être intelligent dans la réorganisation des requêtes pour l'aider à le faire!). Alors que les données circulent de droite à gauche selon les flèches de l'affichage du plan de requête standard, la logique s'exécute de gauche à droite et chaque étape (de droite à gauche) n'est pas nécessairement exécutée avant la prochaine peut commencer. Dans l'exemple simple ci-dessus, si vous regardez chaque bloc du plan de requête en tant qu'agent, l'agent SELECT
demande à l'agent TOP
une ligne qui à son tour demande à l'agent TABLE SCAN
pour l'un, alors l'agent SELECT
en demande un autre, mais l'agent TOP
sait qu'il n'y a pas besoin de se soucier même de demander au lecteur de table, l'agent SELECT
obtient un réponse "plus rien n'est pertinent" et sait que tout le travail est fait. De nombreuses opérations bloquent ce type d'optimisation bien sûr si souvent dans des exemples plus compliqués qu'une analyse de table/index lit vraiment chaque ligne, mais attention à ne pas sauter à la conclusion que tout scan doit être une opération coûteuse.
Généralement, les recherches sont bonnes, les analyses sont mauvaises.
Les recherches sont l'endroit où la requête est capable d'utiliser efficacement l'index et de l'utiliser pour trouver les lignes dont il a besoin.
Les analyses sont l'endroit où la requête examine l'intégralité de l'index en essayant de trouver ce dont elle a besoin.
Comment SQL choisit-il? Au plus profond des internes de l'optimiseur de requêtes, la décision est prise en fonction de votre requête et des index disponibles et des informations statistiques associées à ces index.
Il y a quelques livres à lire qui pourraient être intéressants ici - Les deux de la librairie Red-Gate à http://www.red-gate.com/community/books/
Si vous souhaitez creuser le sujet, un livre très utile (au moins pour moi) est SQL Server Execution Plans de Grant Fritchey, disponible gratuitement sur RedGate ici .
Si vous avez une requête telle que
SELECT *
FROM myTable
SQL Server utilisera probablement une analyse d'index, car il doit parcourir toutes les lignes pour afficher les résultats requis.
Au contraire,
SELECT *
FROM myTable
WHERE myID = 1
entraînera certainement une recherche d'indice. SQL Server utilisera la structure B-tree de l'index myID et la récupération de la ligne appropriée sera beaucoup plus rapide.
D'autres ont assez bien défini les différences entre la recherche et le scan. Dans ce cas, votre requête elle-même et le planificateur d'exécution devraient vous fournir les informations dont vous avez besoin pour voir quelles valeurs sont utilisées comme prédicats (filtres) pour la requête dans chaque partie. En règle générale, il est recommandé d'ajouter toujours des index non clusterisés sur les clés étrangères, et en fonction des cas d'utilisation dans le code du programme, vous souhaiterez peut-être envisager de créer des index multi-colonnes supplémentaires ou des index de colonnes inclus. Avec la terminologie présentée ici, une recherche Google donnera des résultats décents sur des exemples sur chacun.
Mais à titre d'exemple, supposons que votre code interroge la colonne A et la colonne B sur des filtres donnés, mais que vous souhaitiez également renvoyer les valeurs de la colonne C et de la colonne E, vous souhaiterez peut-être créer un index sur les colonnes A et B avec INCLUDE option contenant les colonnes C et E. De cette façon, une recherche d'index unique retournera tout ce dont vous avez besoin, car il n'est pas nécessaire de faire une recherche afin de récupérer les autres valeurs (C et E) sur la même ligne.