Comment supprimer un mauvais plan spécifique du cache de requêtes SQL Server?

33

Nous avons une requête SQL Server 2008 particulière (pas un proc stocké, mais la même chaîne SQL - s'exécute toutes les 5 minutes) qui met en cache par intermittence un très mauvais plan de requête.

Cette requête s'exécute normalement en quelques millisecondes, mais avec ce mauvais plan de requête, cela prend plus de 30 secondes.

Comment supprimer chirurgicalement le seul plan de requête mis en cache incorrect de SQL Server 2008, sans effacer l'intégralité du cache de requêtes sur le serveur de base de données de production?

Jeff Atwood
la source

Réponses:

39

J'ai compris quelques trucs

select * from sys.dm_exec_query_stats

affichera tous les plans de requête mis en cache. Malheureusement, aucun texte SQL n'y est affiché.

Cependant, vous pouvez joindre le texte SQL aux plans comme suit:

select plan_handle, creation_time, last_execution_time, execution_count, qt.text
FROM 
   sys.dm_exec_query_stats qs
   CROSS APPLY sys.dm_exec_sql_text (qs.[sql_handle]) AS qt

À partir d'ici, il est assez simple d'ajouter une WHEREclause pour trouver le SQL que je connais dans la requête, puis je peux exécuter:

DBCC FREEPROCCACHE (plan_handle_id_goes_here)

pour supprimer chaque plan de requête du cache du plan de requête. Pas vraiment facile ni pratique, mais cela semble fonctionner.

edit: vider l' intégralité du cache de requête fonctionnera également et est moins dangereux qu'il n'y paraît, du moins d'après mon expérience:

DBCC FREESYSTEMCACHE ('ALL') WITH MARK_IN_USE_FOR_REMOVAL;
Jeff Atwood
la source
2
le conseil d'utiliser un indice de plan n'en demeure pas moins.
Remus Rusanu
1
J'ai trouvé cela après que ma requête ait actualisé comme par magie c'est un mauvais plan mais je prévois de le tester la prochaine fois. Un indice de plan n'aide pas si la requête souffre de 'optional-itis' - où elle a de nombreux paramètres facultatifs et elle a été optimisée pour un ensemble, puis exécutée pour un autre ensemble. Aucun plan optimal ne peut être associé à ce type de requête. Il existe un plan optimal pour un ensemble de paramètres qui est à son tour affreux pour un autre ensemble de paramètres.
Nick.McDermaid
6

Si vous savez à quoi ressemble le bon plan, utilisez simplement un indice de plan .

Vous ne pouvez pas supprimer une entrée de cache spécifique, mais vous pouvez nettoyer un pool de cache complet avec DBCC FREESYSTEMCACHE(cachename/poolname).

Vous pouvez obtenir le nom de cache d'un mauvais plan de requête si vous disposez du descripteur de plan (à partir de sys.dm_exec_requests.plan_handle pour le session_id en difficulté pendant l'exécution, ou à partir de sys.dm_exec_query_stats après l'exécution):

select ce.name
from sys.dm_exec_cached_plans cp
join sys.dm_os_memory_cache_entries ce on cp.memory_object_address = ce.memory_object_address
where cp.plan_handle = @bad_plan

Cependant, tous les plans SQL ont le nom «Plans SQL», ce qui rend le choix du bon pour DBCC FREESYSTEMCACHE un choix difficile.

Mise à jour

Peu importe, oublié DBCC FREEPROCCACHE(plan_handle), oui cela fonctionnera.

Remus Rusanu
la source
1
La possibilité de passer un plan_handle à DBCC FREEPROCCACHE est disponible dans SQL Server 2008 et non dans SQL Server 2005.
Mario
Qu'est-ce que cela signifie s'il sys.dm_exec_cached_plansn'y a aucune entrée pour le plan_handlefrom sys.dm_exec_requests?
Jonathan Gilbert
@JonathanGilbert, cela signifie que le plan n'a pas été mis en cache ou qu'il a été expulsé du cache. Voir docs.microsoft.com/en-us/sql/relational-databases/…
Remus Rusanu
Donc, juste pour confirmer, même si je viens juste de commencer à exécuter cette requête , et que la requête n'a aucune indication disant de ne pas la mettre en cache, elle peut être mise en cache car SQL Server a pris la décision de ne pas la mettre en cache? Ce ne serait pas parce qu'il fonctionne toujours, non? S'il décide de mettre en cache le plan, il serait mis en cache dès le moment où la requête commence à s'exécuter?
Jonathan Gilbert
1

La solution FREEPROCCACHE est très bien, mais une façon plus directe de le faire est d'utiliser OPTION (RECOMPILE) sur votre chaîne SQL (vous avez mentionné que ce n'était pas un SP), cela indique au moteur que c'est un plan à usage unique, car vous suspectez probablement il y a un reniflage de paramètres ou vos statistiques sont radicalement différentes d'une exécution à l'autre et vous pensez que c'est un problème de plan mis en cache.

DECLARE @SQL NVARCHAR(4000)
SELECT @SQL = 'SELECT * FROM Table WHERE Column LIKE @NAME OPTION (RECOMPILE)'
EXEC sp_executesql @SQL, N'@NAME varchar(15)', 'MyName' 
CodeCowboyOrg
la source