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

SQL Server - reniflage de paramètres

C'est bien mais ça peut être mal parfois.

Le reniflage de paramètres consiste à ce que l'optimiseur de requête utilise la valeur du paramètre fourni pour déterminer le meilleur plan de requête possible. L'un des nombreux choix et celui qui est assez facile à comprendre est de savoir si la table entière doit être analysée pour obtenir les valeurs ou si ce sera plus rapide en utilisant les recherches d'index. Si la valeur de votre paramètre est très sélective, l'optimiseur construira probablement un plan de requête avec des recherches et si ce n'est pas le cas, la requête effectuera une analyse de votre table.

Le plan de requête est ensuite mis en cache et réutilisé pour des requêtes consécutives qui ont des valeurs différentes. La mauvaise partie du reniflage de paramètres est lorsque le plan mis en cache n'est pas le meilleur choix pour l'une de ces valeurs.

Exemple de données :

create table T
(
  ID int identity primary key,
  Value int not null,
  AnotherValue int null
);

create index IX_T_Value on T(Value);

insert into T(Value) values(1);

insert into T(Value)
select 2
from sys.all_objects;

T est une table avec quelques milliers de lignes avec un index non clusterisé sur Value. Il y a une ligne où la valeur est 1 et le reste a la valeur 2 .

Exemple de requête :

select *
from T 
where Value = @Value;

Les choix que l'optimiseur de requête a ici sont soit d'effectuer une analyse d'index clusterisée et de vérifier la clause where par rapport à chaque ligne, soit d'utiliser une recherche d'index pour rechercher les lignes qui correspondent, puis d'effectuer une recherche de clé pour obtenir les valeurs des colonnes demandées dans la liste des colonnes.

Lorsque la valeur reniflée est 1 le plan de requête ressemblera à ceci :

Et quand la valeur reniflée est 2 il ressemblera à ceci :

La mauvaise partie du reniflage des paramètres dans ce cas se produit lorsque le plan de requête est construit en reniflant un 1 mais exécuté plus tard avec la valeur de 2 .

Vous pouvez voir que la recherche de clé a été exécutée 2352 fois. Un scan serait clairement le meilleur choix.

Pour résumer, je dirais que le reniflage de paramètres est une bonne chose que vous devriez essayer de réaliser autant que possible en utilisant des paramètres pour vos requêtes. Parfois, cela peut mal tourner et, dans ces cas, cela est probablement dû à des données biaisées qui perturbent vos statistiques.

Mise à jour :

Voici une requête contre quelques dmv que vous pouvez utiliser pour trouver les requêtes les plus coûteuses sur votre système. Passez à l'ordre par clause pour utiliser différents critères sur ce que vous recherchez. Je pense que TotalDuration est un bon point de départ.

set transaction isolation level read uncommitted;

select top(10)
  PlanCreated       = qs.creation_time,
  ObjectName        = object_name(st.objectid),
  QueryPlan         = cast(qp.query_plan as xml),
  QueryText         = substring(st.text, 1 + (qs.statement_start_offset / 2), 1 + ((isnull(nullif(qs.statement_end_offset, -1), datalength(st.text)) - qs.statement_start_offset) / 2)),
  ExecutionCount    = qs.execution_count,
  TotalRW           = qs.total_logical_reads + qs.total_logical_writes,
  AvgRW             = (qs.total_logical_reads + qs.total_logical_writes) / qs.execution_count,
  TotalDurationMS   = qs.total_elapsed_time / 1000,
  AvgDurationMS     = qs.total_elapsed_time / qs.execution_count / 1000,
  TotalCPUMS        = qs.total_worker_time / 1000,
  AvgCPUMS          = qs.total_worker_time / qs.execution_count / 1000,
  TotalCLRMS        = qs.total_clr_time / 1000,
  AvgCLRMS          = qs.total_clr_time / qs.execution_count / 1000,
  TotalRows         = qs.total_rows,
  AvgRows           = qs.total_rows / qs.execution_count
from sys.dm_exec_query_stats as qs
  cross apply sys.dm_exec_sql_text(qs.sql_handle) as st
  cross apply sys.dm_exec_text_query_plan(qs.plan_handle, qs.statement_start_offset, qs.statement_end_offset) as qp
--order by ExecutionCount desc
--order by TotalRW desc
order by TotalDurationMS desc
--order by AvgDurationMS desc
;