Optimiser les requêtes SQL
Quelle méthodologie pouvez-vous utiliser pour optimiser vos requêtes Transact-SQL ?
Hypothèses
Les problèmes de performance de requêtes peuvent provenir de :
- Un mauvais modèle de données ;
- De requêtes mal écrites qui sont trop longues ;
- D’un comportement unitaire et non ensembliste (RBAR) ;
- D’exécution de triggers, de transactions trop longues;
- D’un manque d’index;
- D’attentes, par exemple sur le parallélisme des requêtes;
- De problèmes de plans d’exécution, de compilation et de parameter sniffing.
Diagnostiquer les requêtes mal écrites et trop longues
Il est difficile de corriger un mauvais modèle de données. Vous pouvez :
- Utiliser la compression ROW ou PAGE pour diminuer la taille physique des tables et améliorer les IO.
- Corriger petit à petit le schéma en utilisant des vues comme couche d’abstraction pour remodéliser les tables sans avoir à modifier le code client. Les mises à jour sur les vues peuvent être reproduites avec des déclencheurs INSTEAD OF.
Diagnostiquer les requêtes mal écrites et trop longues
- Vérifiez les statistiques d’IO et de temps des requêtes
SET STATISTICS IO ON
– affiche les statistiques d’IO par table accédée dans la requête. L’unité est la page (8 ko). Les statistiques sont séparées en IO logiques (dans le buffer) et IO physiques (lectures physiques et anticipées).SET STATISTICS TIME ON
– affiche les statistiques de temps par requête. Les statistiques sont séparées en temps de compilation (création du plan d’exécution) et d’exécution. Les temps sont le temps CPU et la durée totale de l’exécution, envoi des résultats au client compris.
- Utilisez le Query Store pour identifier les requêtes les plus consommatrices.
- Utilisez une session d’évènements étendus :
- Exemples de sessions sur mon Github
- Pour apprendre à utiliser les évènements étendus, vous trouvez sur ma chaîne Youtube une vidéo complète sur le sujet.
- Requêtez la vue dm_exec_query_stats
- Utilisez, en temps réel, la procédure sp_whoisactive
- Si vous avez des procédures stockées, utilisez la vue dm_exec_procedure_stats
Comportement unitaire
Trop d’allers-retours entre le client et le serveur pose de grands problèmes de performances. On a appelé ce comportement RBAR : Row By Agonizing Row.
Le comportement souhaité est : obtenir le plus de données possibles dans une requête ensembliste, et effectuer le moins d’allers-retours possibles. Comme quand vous faites vos courses : Vous achetez tout en une fois au supermarché, et vous revenez chez vous avec toutes vos courses.
- Cherchez, avec une session d’évènements étendus sans filtre de coût de requête, des appels répétitifs de requêtes semblables, par exemple des suites d’inserts ou des
SELECT
unitaires semblables avec des changements de paramètres. - Requêtez la vue dm_exec_query_stats en cherchant des requêtes peu consommatrices mais exécutées très souvent (triez sur la colonne
execution_count
) - Utilisez le Query Store pour identifier les requêtes les plus exécutées.
Pour cela :
- Envoyez des tableaux à vos procédures stockées. Vous avez plusieurs méthodes :
- Un paramètre XML, parsé avec
NODES()
; - Un paramètre en JSON (
NVARCHAR(MAX)
), parsé avecOPENJSON
; - Un paramètre en chaîne de caractères, parsé avec
STRING_SPLIT()
; - Un paramètre en type table ;
- Un paramètre XML, parsé avec
- Vérifiez toujours le comportement de votre application avec une session d’évènements étendus sur le serveur.
- En Entity Framework, assurez-vous que vous utilisez le bon loading.
Parallélisme
-
Pour optimiser le parallélisme sur l’instance, Configurer les éléments suivants :
- Seuil de coût pour le parallélisme – à mettre à 50
- Degré maximum de parallélisme – ne pas le laisser à 0 si vous avez plus de huit processeurs. 4 est souvent une bonne valeur pour les serveurs OLTP. Voir les recommandations
-
Depuis SQL Server 2016, vous pouvez configurer le degré de parallélisme par base de données, à l’aide d’une configuration scopée.
ALTER DATABASE SCOPED CONFIGURATION SET MAXDOP = 1 ;
Blocages dus à trop de verrouillage
Les blocages sont des attentes sur des verrous posés par d’autres sessions.
Y a-t-il des blocages ?
- Interrogez la requête suivante : Paul Randall, tell me where it hurts et cherchez les attentes de type
LCK...
- Si vous trouvez des attentes de ce type, approfondissez le diagnostic à l’aide des instructions suivantes.
- Vous pouvez aussi planifier une vérification toutes les quelques minutes avec le script suivant.
- Surveillez les transactions actives et les longues transactions. Vous pouvez utiliser cette requête sur mon Github pour voir les transactions actives.
Exécution de triggers, de transactions trop longues
- Identifiez le coût de vos déclencheurs à l’aide de la vue de diagnostic sys.dm_exec_triggers_stats
- Cherchez les blocages éventuels avec les outils de la section précédente.
- Si vous avez des procédures stockées, utilisez la vue dm_exec_procedure_stats
- Si vous soupçonnez des déclencheurs, des procédures stockées ou des appels de fonctions, créez une session d’évènements étendus avec l’évènement
sp_statement_completed
– code pour Azure à adapter pour on prem
Manque d’index
- Vérifiez le rapport d’index manquants.
- Vérifiez que les index sont bien utilisés avec le rapport d’utilisation des index.
- Vous pouvez vous référer à l’article suivant.
- Utilisez le Database Tuning Advisor sur une requête ou une charge de travail.
- Si vous avez des requêtes plutôt analytiques, essayez les index ColumnStore.
Problèmes de plans d’exécution, de compilation et de parameter sniffing
Lorsque les problèmes se posent, videz le cache de plans à l’aide de la commande suivante : DBCC FREEPROCCACHE
. Est-ce que cela résout le problème ? Vous pouvez exécuter cette commande au lieu de redémarrer un serveur SQL.
Vérifiez les statistiques : - requête de diagnostic
Erreur d’estimation de cardinalité
- un signe classique : la requête se dégrade avec le temps. Planifiez un recalcul des statistiques plus régulièrement.
- vérifiez les statistiques avec ce script, regardez les tables qui ont eu beaucoup de modifications.
- regardez le plan d’exécution actuel
- Utilisez plan explorer
- lancez un recalcul avec
UPDATE STATISTICS
- Activez l’ancien moteur d’estimation de cardinalité.
- ajoutez l’option dans la requête
OPTION (USE HINT('FORCE_LEGACY_CARDINALITY_ESTIMATION'))
- regardez si on utilise des variables de type table