Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Composants internes de SQL Server :plan de mise en cache Pt. I – Réutilisation des plans

SQL Server existe depuis plus de 30 ans, et je travaille avec SQL Server depuis presque aussi longtemps. J'ai vu beaucoup de changements au fil des années (et des décennies !) et des versions de cet incroyable produit. Dans ces articles, je vais partager avec vous comment j'examine certaines des fonctionnalités ou certains aspects de SQL Server, parfois avec un peu de perspective historique.

Consultez les blogs récents de Kalen sur les opérateurs problématiques ici.

Les plans de diagnostic de serveur SQL peuvent être coûteux à créer, car l'optimiseur de requête doit être en mesure de trouver un bon plan pour toute requête légale soumise. L'optimiseur évalue plusieurs ordres de jointure, plusieurs index. et divers types d'algorithmes de jointure et de regroupement, en fonction de votre requête et des tables impliquées. Si la même requête est réexécutée, SQL Server peut économiser beaucoup de ressources en réutilisant un plan existant. Mais il n'est pas toujours possible de réutiliser un plan existant, et ce n'est pas toujours une bonne chose de le faire. Dans les deux prochains articles, nous verrons quand un plan est réutilisé et quand il est recompilé.

Tout d'abord, nous examinerons les différentes saveurs de plans et la vue des métadonnées que j'utilise le plus souvent pour voir ce qu'il y a dans le cache de mon plan. J'ai rédigé mon propre point de vue qui fournit les informations que je trouve les plus utiles le plus souvent. SQL Server met en cache six types différents de plans de requête, mais seuls deux sont normalement utilisés pour le réglage du cache de plan. Il s'agit du PLAN COMPILÉ et du TALON DE PLAN COMPILÉ. Ma vue filtre tous sauf ces deux types d'objets de cache. Les PLANS COMPILÉS se déclinent en trois variétés :AD HOC, PRÉPARÉ et PROC. Je commenterai les trois types.

Même si nous ne regardons que les PLANS COMPILÉS, il y a encore beaucoup de plans dans le cache qui doivent généralement être ignorés, car ils sont générés par SQL Server lui-même. Il s'agit notamment de plans de recherche d'index de recherche de flux de fichiers ou de texte intégral ou de requêtes internes fonctionnant avec OLTP en mémoire. Donc, ma vue ajoute des filtres pour essayer de sevrer la plupart des plans qui ne m'intéressent pas. Vous pouvez télécharger un script pour construire cette vue, appelé sp_cacheobjects , d'ici.

Même avec tous les filtres utilisés par ma vue, certaines requêtes internes de SQL Server sont toujours en cache; J'ai l'habitude de vider fréquemment le cache du plan lorsque je fais des tests dans ce domaine. Le moyen le plus simple d'effacer TOUS les plans du cache consiste à utiliser la commande :DBCC FREEPROCCACHE.

Plans compilés ad hoc

Le type de plan le plus simple est Adhoc. Ceci est utilisé pour les requêtes de base qui ne rentrent pas dans une autre catégorie. Si vous avez téléchargé mon script et créé ma vue sp_cacheobjects, vous pouvez exécuter ce qui suit. N'importe quelle version de la base de données AdventureWorks devrait fonctionner. Ce script fait une copie d'une table et construit quelques index uniques dessus. Il masse également le montant du sous-total pour supprimer tous les chiffres décimaux.

USE AdventureWorks2016;
GO
DROP TABLE IF EXISTS newsales;
GO
-- Make a copy of the Sales.SalesOrderHeader table
SELECT * INTO dbo.newsales
FROM Sales.SalesOrderHeader;
GO
UPDATE dbo.newsales
SET SubTotal = cast(cast(SubTotal as int) as money);
GO
CREATE UNIQUE index newsales_ident
    ON newsales(SalesOrderID);
GO
CREATE INDEX IX_Sales_SubTotal ON newsales(SubTotal);
GO
-- Adhoc query plan reuse
DBCC FREEPROCCACHE;
GO
-- adhoc query
SELECT * FROM dbo.newsales
WHERE SubTotal = 4;
GO
SELECT * FROM sp_cacheobjects;
GO

Dans ma sortie, vous voyez deux plans Adhoc. L'un est pour l'instruction SELECT de la newsales table, et l'autre est pour le SELECT de mes sp_cacheobjects voir. Étant donné que le plan est mis en cache, si EXACTEMENT la même requête est exécutée à nouveau, le même plan peut être réutilisé et vous verrez les usecounts augmentation de valeur. Cependant, il y a un hic. Pour qu'un plan Adhoc soit réutilisé, la chaîne SQL doit être absolument identique. Si vous modifiez des caractères dans le SQL, la requête n'est pas reconnue comme la même requête et un nouveau plan est généré. Si j'ajoute même un espace, inclut le commentaire ou un nouveau saut de ligne, ce n'est pas la même chaîne. Si je change la casse, cela signifie qu'il y a différentes valeurs de code ASCII, donc pas la même chaîne.

Vous pouvez essayer cela vous-même en exécutant différentes variantes de ma première instruction SELECT à partir des newsales table. Vous verrez une ligne différente dans le cache pour chacun. Après avoir exécuté plusieurs variantes - en modifiant le numéro que je recherchais, en modifiant la casse, en ajoutant le commentaire et une nouvelle ligne, je vois ce qui suit dans le cache. Le SELECT de ma vue est réutilisé, mais tout le reste a un usecounts valeur de 1.

Une exigence supplémentaire pour la réutilisation du plan de requête ad hoc est que la session exécutant la requête doit avoir les mêmes options SET en vigueur . Il y a une autre colonne dans la sortie que vous pouvez voir à droite du texte de la requête, appelée SETOPTS. Il s'agit d'une chaîne de bits avec un bit pour chaque option SET pertinente. Si vous modifiez l'une des options, par exemple, SET ANSI_NULLS OFF, la chaîne de bits changera et le même plan avec la chaîne de bits d'origine ne pourra pas être réutilisé.

Plans compilés préparés

Le deuxième type de plan compilé en cache est un plan PRÉPARÉ. Si votre requête répond à un certain ensemble d'exigences. Il peut effectivement être paramétré automatiquement. Il apparaît dans les métadonnées comme PREPARED et la chaîne SQL affiche un marqueur de paramètre. Voici un exemple :

Le plan PRÉPARÉ affiche le marqueur de paramètre sous la forme @1 et n'inclut pas la valeur réelle. Notez qu'il y a une ligne pour une requête ADHOC avec une valeur réelle de 5555, mais ce n'est en fait qu'un "shell" de la vraie requête. Il ne met pas en cache le plan entier mais seulement la requête et quelques détails d'identification, pour aider le processeur de requêtes à trouver le plan paramétré dans le cache. Notez la taille (pagesutilisées ) est beaucoup plus petit que le plan PRÉPARÉ.

Le mode de paramétrage par défaut, appelé paramétrage SIMPLE, est extrêmement strict sur les plans qui peuvent être paramétrés. Ce n'est vraiment que la plus simple des requêtes paramétrables par défaut. Les requêtes qui contiennent JOIN, GROUP BY, OR et de nombreuses autres constructions de requête relativement courantes empêchent une requête d'être paramétrée. En plus de n'avoir aucune de ces constructions, la chose la plus importante pour le paramétrage SIMPLE est que la requête est SAFE. Cela signifie qu'il n'y a qu'un seul plan possible, quelles que soient les valeurs transmises pour les paramètres. (Bien sûr, une requête sans aucun paramètre pourrait également être SAFE.) Ma requête recherche une correspondance exacte sur la colonne SalesOrderID , qui contient un index unique. Ainsi, l'index non cluster existant pourrait être utilisé pour trouver n'importe quelle ligne correspondante. Quelle que soit la valeur que j'utilise, 55555 ou autre chose, il n'y aura jamais plus d'une ligne, ce qui signifie que le plan sera toujours bon.

Dans mon exemple de plan de requête ad hoc, je cherchais des valeurs correspondantes pour SubTotal . Quelques Sous-Totaux les valeurs se produisent quelques fois ou pas du tout, donc un index non clusterisé serait bon. Mais d'autres valeurs pourraient se produire plusieurs fois, donc l'index ne serait PAS utile. Ainsi, le plan de requête n'est pas SAFE et la requête ne peut pas être paramétrée. C'est pourquoi nous avons vu un plan Adhoc pour mon premier exemple.

SI vous avez des requêtes avec JOIN ou d'autres constructions non autorisées, vous pouvez indiquer à SQL Server d'être plus agressif dans le paramétrage en modifiant une option de base de données :

ALTER DATABASE AdventureWorks2016 SET parameterization FORCED;
GO

Définir votre base de données sur le paramétrage FORCÉ signifie que SQL Server paramétrera beaucoup plus de requêtes, y compris celles avec JOIN, GROUP BY, OR, etc. Mais cela signifie également que SQL Server peut paramétrer une requête qui n'est pas SAFE. Il peut proposer un plan qui est bon lorsque seules quelques lignes sont renvoyées, puis réutiliser le plan lorsque de nombreuses lignes sont renvoyées. Cela peut aboutir à des performances très sous-optimales.

Une dernière option pour un plan préparé consiste à préparer explicitement un plan. Ce comportement est généralement invoqué via une application avec SQLPrepare et SQLExecute Apis. Vous spécifiez quelle est la requête avec des marquages ​​de paramètre, vous spécifiez les types de données et vous spécifiez les valeurs spécifiques à utiliser. La même requête peut alors être exécutée à nouveau avec des valeurs spécifiques différentes et le plan existant sera utilisé. Bien que l'utilisation de plans explicitement préparés puisse être possible dans les cas où SQL Server ne paramètre pas et que vous le souhaitez, cela n'empêche pas SQL Server d'utiliser un plan qui n'est PAS bon pour les paramètres suivants. Vous devez tester vos requêtes avec de nombreuses valeurs d'entrée différentes et vous assurer d'obtenir les performances attendues si et quand un plan est réutilisé.

Les métadonnées (par exemple, mes sp_cacheobjects view) affiche simplement PREPARED pour les trois types de plans :paramétrage automatique FORCÉ et SIMPLE et paramétrage EXPLICITE.

Procéder aux plans compilés

Le dernier type d'objet la valeur pour Compiled Plans est pour une procédure stockée, qui est affichée comme Proc. Lorsque cela est possible, les procédures stockées sont le meilleur choix pour le code réutilisable, en raison de leur facilité de gestion depuis le serveur lui-même, mais cela ne signifie pas qu'elles sont toujours garanties d'offrir les meilleures performances. Tout comme l'utilisation de l'option de paramétrage FORCED (ainsi que le paramétrage explicite), les procédures stockées utilisent le "reniflage de paramètres". Cela signifie que la première valeur de paramètre transmise détermine le plan. Si les exécutions suivantes fonctionnent correctement avec le même plan, le reniflage des paramètres n'est pas un problème et peut en fait être bénéfique car il nous permet d'économiser le coût de la recompilation et de la réoptimisation. Cependant, si les exécutions suivantes avec des valeurs différentes ne doivent pas utiliser le plan d'origine, nous avons un problème. Je vais vous montrer un exemple de reniflage de paramètre causant un problème

Je vais créer une procédure stockée basée sur les nouvelles ventes tableau que nous avons utilisé précédemment. La procédure aura une seule requête, qui filtre en fonction de SalesOrderID colonne, sur laquelle nous avons construit un index non clusterisé. La requête sera basée sur une inégalité, donc pour certaines valeurs, la requête peut renvoyer seulement quelques lignes et utiliser l'index, et pour d'autres valeurs, la requête peut renvoyer BEAUCOUP de lignes. En d'autres termes, la requête n'est pas SÛRE.

USE AdventureWorks2016;
GO
DROP PROC IF EXISTS get_sales_range;
GO
CREATE PROC get_sales_range
   @num int
AS
    SELECT * FROM dbo.newsales
    WHERE SalesOrderID < @num;
GO

J'utiliserai l'option SET STATISTICS IO ON pour voir combien de travail est effectué lorsque la procédure est exécutée. Je vais d'abord l'exécuter avec un paramètre qui ne renvoie que quelques lignes :

SET STATISTICS IO ON
GO
EXEC get_sales_range 43700;
GO

La valeur STATISTICS IO signale qu'il a fallu 43 lectures logiques pour renvoyer 41 lignes. Ceci est normal pour un index non clusterisé. Maintenant, nous exécutons à nouveau la procédure avec une valeur beaucoup plus grande.

EXEC get_sales_range 66666;
GO
SELECT * FROM sp_cacheobjects;
GO
This time, we see that SQL Server used a whole lot more reads:

En fait, un tableau scanné sur les newsales table ne prend que 843 lectures, ce sont donc des performances bien inférieures à celles d'un balayage de table. Les sp_cacheobjects La vue nous montre que le plan PROC a été réutilisé pour cette deuxième exécution. Ceci est un exemple de cas où le reniflage de paramètres n'est PAS une bonne chose.

Alors, que pouvons-nous faire lorsque le reniflage de paramètres est un problème ? Dans le prochain article, je vous dirai quand SQL Server proposera un nouveau plan et ne réutilisera pas les anciens. Nous verrons comment vous pouvez forcer (ou encourager) la recompilation, et nous verrons également quand SQL Server recompilera automatiquement vos requêtes.

Spotlight Cloud peut révolutionner votre surveillance des performances et vos diagnostics de serveur SQL. Commencez votre essai gratuit en utilisant le lien ci-dessous :