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

Paramétrage simple et plans triviaux — Partie 1

Ceci est la première partie d'une série sur le paramétrage simple et plans triviaux . Ces deux fonctionnalités de compilation sont étroitement liées et ont des objectifs similaires. Ciblez à la fois les performances et l'efficacité pour les charges de travail qui soumettent fréquemment des instructions simples.

Malgré les noms « simples » et « triviaux », les deux ont des comportements subtils et des détails de mise en œuvre qui peuvent rendre leur fonctionnement difficile à comprendre. Cette série ne s'attarde pas trop sur les bases mais se concentre sur des aspects moins connus susceptibles de faire trébucher même les professionnels des bases de données les plus expérimentés.

Dans cette première partie, après une rapide introduction, j'examine les effets du paramétrage simple sur le cache du plan.

Paramétrage simple

Il est presque toujours préférable de paramétrer explicitement déclarations, plutôt que de compter sur le serveur pour le faire. Être explicite vous donne un contrôle total sur tous les aspects du processus de paramétrage, y compris l'endroit où les paramètres sont utilisés, les types de données précis utilisés et le moment où les plans sont réutilisés.

La plupart des clients et des pilotes fournissent des moyens spécifiques d'utiliser le paramétrage explicite. Il existe également des options comme sp_executesql , procédures stockées et fonctions.

Je ne vais pas aborder les problèmes liés au reniflage de paramètres ou à l'injection SQL car, bien qu'importants, ils ne sont pas au centre de cette série. Néanmoins, vous devriez écrire du code avec les deux à l'esprit.

Pour les applications héritées ou d'autres codes tiers qui ne peuvent pas être facilement modifiés, le paramétrage explicite n'est pas toujours possible. Vous pourrez peut-être surmonter certains obstacles en utilisant des guides de plan de modèle. Dans tous les cas, il s'agirait d'une charge de travail inhabituelle qui ne contiendrait pas au moins certaines instructions paramétrées côté serveur.

Forfaits Shell

Lorsque SQL Server 2005 a introduit le paramétrage forcé , le paramétrage automatique existant la fonctionnalité a été renommée Paramétrage simple . Malgré le changement de terminologie, paramétrage simple fonctionne de la même manière que le paramétrage automatique toujours fait :SQL Server tente de remplacer les valeurs littérales constantes dans les instructions ad hoc par des marqueurs de paramètres. L'objectif est de réduire les compilations en augmentant la réutilisation des plans mis en cache.

Regardons un exemple, en utilisant la base de données Stack Overflow 2010 sur SQL Server 2019 CU 14. La compatibilité de la base de données est définie sur 150, et le seuil de coût pour le parallélisme est défini sur 50 pour éviter le parallélisme pour le moment :

EXECUTE sys.sp_configure
    @configname = 'show advanced options',
    @configvalue = 1;
RECONFIGURE;
GO
EXECUTE sys.sp_configure
    @configname = 'cost threshold for parallelism',
    @configvalue = 50;
RECONFIGURE;

Exemple de code :

-- Clear the cache of plans for this database
ALTER DATABASE SCOPED CONFIGURATION 
    CLEAR PROCEDURE_CACHE;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2521;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2827;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3144;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3151;
GO

Ces instructions comportent des prédicats qui ne diffèrent que par leurs valeurs littérales constantes. SQL Server applique avec succès le paramétrage simple , aboutissant à un plan paramétré. Le plan paramétré unique est utilisé quatre fois comme nous pouvons le voir en interrogeant le cache du plan :

SELECT
    CP.usecounts,
    CP.cacheobjtype,
    CP.objtype,
    CP.size_in_bytes,
    ST.[text],
    QP.query_plan
FROM sys.dm_exec_cached_plans AS CP
OUTER APPLY sys.dm_exec_sql_text (CP.plan_handle) AS ST
OUTER APPLY sys.dm_exec_query_plan (CP.plan_handle) AS QP
WHERE 
    ST.[text] NOT LIKE '%dm_exec_cached_plans%'
    AND ST.[text] LIKE '%DisplayName%Users%'
ORDER BY 
    CP.usecounts ASC;

Les résultats montrent un Adhoc planifiez une entrée de cache pour chaque déclaration d'origine et un seul Préparé planifier :

Quatre plans ad hoc et un plan préparé

Un préparé est similaire à une procédure stockée, avec des paramètres déduits des valeurs littérales trouvées dans le Adhoc déclaration. Je mentionne cela comme un modèle mental utile lorsque je réfléchis au processus de paramétrage côté serveur.

Notez que SQL Server met en cache les deux le texte original et la forme paramétrée. Lorsque le paramétrage simple est réussi, le plan associé au texte d'origine est Adhoc et ne contient pas de plan d'exécution complet. Au lieu de cela, le plan mis en cache est un shell avec très peu à part un pointeur vers Préparé plan paramétré.

La représentation XML des plans shell contenir du texte comme :

<ShowPlanXML xmlns="http://schemas.microsoft.com/sqlserver/2004/07/showplan" Version="1.539" Build="15.0.4188.2">
<BatchSequence>
<Batch>
<Statements>
<StmtSimple 
  StatementText="SELECT U.DisplayName&#xD;&#xA;FROM dbo.Users AS U &#xD;&#xA;WHERE U.Reputation = 3151"
  StatementId="1" 
  StatementCompId="1" 
  StatementType="SELECT" 
  RetrievedFromCache="true" 
  ParameterizedPlanHandle="0x0600050090C8321CE04B4B079E01000001000000000000000000000000000000000000000000000000000000" 
  ParameterizedText="(@1 smallint)SELECT [U].[DisplayName] FROM [dbo].[Users] [U] WHERE [U].[Reputation]=@1" />
</Statements>
</Batch>
</BatchSequence>
</ShowPlanXML>

C'est tout le plan. Le ParameterizedPlanHandle points de l'Adhoc shell au plan entièrement paramétré. La valeur de l'identificateur est la même pour les quatre plans de coque.

Stubs de plan

Les plans Shell sont plus petits qu'un plan compilé complet :16 Ko au lieu de 40 Ko dans l'exemple. Cela peut encore représenter une quantité importante de mémoire si vous avez de nombreuses instructions utilisant un paramétrage simple ou de nombreuses valeurs de paramètres différentes. La plupart des instances de SQL Server ne sont pas tellement inondées de mémoire qu'elles peuvent se permettre de la gaspiller de cette façon. Les plans de shell sont considérés comme très jetables par SQL Server, mais les trouver et les supprimer consomme des ressources et peut devenir un point de discorde.

Nous pouvons réduire la consommation totale de mémoire pour les plans shell en activant l'option d'optimisation pour les charges de travail ad hoc.

EXECUTE sys.sp_configure
    @configname = 'show advanced options',
    @configvalue = 1;
RECONFIGURE;
GO
EXECUTE sys.sp_configure
    @configname = 'optimize for ad hoc workloads',
    @configvalue = 1;
RECONFIGURE;

Cela met en cache un minuscule stub la première fois qu'une instruction ad hoc est rencontrée au lieu d'un shell. Le stub sert de signet afin que le serveur puisse se rappeler qu'il a déjà vu le texte exact de l'instruction. En rencontrant le même texte une deuxième fois, la compilation et la mise en cache se poursuivent comme si optimisait pour les charges de travail ad hoc n'étaient pas activés.

Réexécuter l'exemple avec optimiser pour les charges de travail ad hoc activé montre l'effet sur le cache du plan.

Stubs de plan compilés

Aucun plan n'est mis en cache pour les instructions ad hoc, juste un stub. Il n'y a pas de ParameterizedPlanHandle pointeur vers Préparé plan, bien qu'un plan paramétré complet existe mis en cache.

L'exécution des lots de test une deuxième fois (sans vider le cache du plan) donne le même résultat que lors de l'optimisation pour les charges de travail ad hoc n'a pas été activé—quatre Adhoc plans shell pointant vers Préparé planifier.

Avant de continuer, réinitialisez l'optimisation pour les charges de travail ad hoc mise à zéro :

EXECUTE sys.sp_configure
    @configname = 'optimize for ad hoc workloads',
    @configvalue = 0;
RECONFIGURE;

Planifier les limites de taille du cache

Que des coquilles de plan ou des talons de plan soient utilisés, il y a toujours des inconvénients à tous ces Adhoc entrées de cache. J'ai déjà mentionné l'utilisation totale de la mémoire, mais chaque cache de plan a également un nombre maximum d'entrées. Même lorsque l'utilisation totale de la mémoire n'est pas un problème, la quantité peut l'être.

Les limites peuvent être augmentées avec l'indicateur de trace documenté 174 (nombre d'entrées) et l'indicateur de trace 8032 (taille totale). Selon la charge de travail et les autres demandes de mémoire, cela peut ne pas être la meilleure solution. Après tout, cela signifie simplement mettre en cache plus d'éléments Adhoc de faible valeur plans, privant la mémoire d'autres besoins.

Mettre en cache uniquement les plans préparés

Si la charge de travail émet rarement des lots ad hoc avec exactement le même texte de déclaration, la mise en cache des shells de plan ou des talons de plan est un gaspillage de ressources. Il consomme de la mémoire et peut provoquer des conflits lorsque les plans SQL magasin de cache (CACHESTORE_SQLCP ) doit être réduit pour tenir dans les limites configurées.

L'idéal serait de paramétrer les lots ad-hoc entrants, mais seulement cache la version paramétrée. Cela a un coût, car les futures instructions ad hoc doivent être paramétrées avant de pouvoir être mises en correspondance avec le plan mis en cache paramétré. D'un autre côté, cela se serait produit de toute façon puisque nous avons déjà indiqué exact les correspondances textuelles sont rares pour la charge de travail cible.

Pour les charges de travail qui bénéficient d'un paramétrage simple, mais pas de la mise en cache de Adhoc entrées, il y a plusieurs options.

Indicateur de trace non documenté

La première option consiste à activer l'indicateur de trace non documenté 253. Cela empêche la mise en cache de Adhoc prévoit complètement. Il ne se contente pas de limiter le nombre de ces plans ou de les empêcher de "rester" dans le cache, comme cela a parfois été suggéré.

L'indicateur de trace 253 peut être activé au niveau de la session, en limitant ses effets à cette seule connexion, ou plus largement en tant qu'indicateur global ou de démarrage. Il fonctionne également comme un indicateur de requête, mais leur utilisation empêche un paramétrage simple, ce qui serait contre-productif ici. Il existe une liste partielle des éléments qui empêchent le paramétrage simple dans le Microsoft Technical Paper, Plan Caching and Recompilation in SQL Server 2012.

Avec l'indicateur de trace 253 actif avant la compilation du lot , seul le Préparé les déclarations sont mises en cache :

ALTER DATABASE SCOPED CONFIGURATION 
    CLEAR PROCEDURE_CACHE;
GO
-- Do not cache ad-hoc plans
DBCC TRACEON (253);
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2521;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2827;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3144;
GO
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3151;
GO
-- Cache ad-hoc plans again
DBCC TRACEOFF (253);
GO

La requête du cache du plan confirme uniquement la valeur Préparé l'instruction est mise en cache et réutilisée.

Seule l'instruction préparée est mise en cache

Le lot impossible à mettre en cache

La deuxième option consiste à inclure une déclaration qui marque l'ensemble du lot comme uncacheable . Les déclarations appropriées sont souvent liées à la sécurité ou sensibles d'une manière ou d'une autre.

Cela peut sembler peu pratique, mais il y a quelques atténuations. Tout d'abord, l'instruction sensible n'a pas besoin d'être exécutée, elle doit simplement être présente . Lorsque cette condition est remplie, l'utilisateur exécutant le lot n'a même pas besoin de permission pour exécuter l'instruction sensible. Attention, l'effet est limité au lot contenant la mention sensible.

Deux instructions suffisamment sensibles et un exemple d'utilisation sont présentés ci-dessous (avec les instructions de test maintenant dans un seul lot) :

ALTER DATABASE SCOPED CONFIGURATION 
    CLEAR PROCEDURE_CACHE;
GO
-- Prevent caching of all statements in this batch.
-- Neither KEY nor CERTIFICATE need to exist.
-- No special permissions are needed.
-- GOTO is used to ensure the statements are not executed.
GOTO Start
    OPEN SYMMETRIC KEY Banana 
        DECRYPTION BY CERTIFICATE Banana;
Start:
 
/* Another way to achieve the same effect without GOTO
IF 1 = 0
BEGIN
    CREATE APPLICATION ROLE Banana 
    WITH PASSWORD = '';
END;
*/
 
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2521;
 
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 2827;
 
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3144;
 
SELECT U.DisplayName
FROM dbo.Users AS U 
WHERE U.Reputation = 3151;
GO

Le préparé plans créés par paramétrage simple sont toujours mis en cache et réutilisés même si le lot parent est marqué comme ne pouvant pas être mis en cache.

Seule l'instruction préparée est mise en cache

Aucune des deux solutions n'est idéale, mais jusqu'à ce que Microsoft fournisse une solution documentée et prise en charge pour ce problème, ce sont les meilleures options que je connaisse.

Fin de la partie 1

Il y a beaucoup plus de terrain à couvrir sur ce sujet. La deuxième partie couvrira les types de données attribués lors du paramétrage simple est employé.