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

Réglage des performances à la va-et-vient :utilisation incorrecte des tables temporaires

Dans cette suite de ma série "réglage des performances par réflexe", j'aimerais discuter de quatre problèmes courants que je vois avec l'utilisation de tables temporaires. N'importe lequel de ces problèmes peut paralyser une charge de travail, il vaut donc la peine de les connaître et de les rechercher dans votre environnement.

Problème 1 :Utiliser des tables temporaires là où elles ne sont pas nécessaires

https://www.flickr. com/photos/tea_time/3890677277/

Les tables temporaires ont diverses utilisations (probablement la plus courante consiste à stocker un jeu de résultats intermédiaire pour une utilisation ultérieure), mais vous devez vous rappeler que lorsque vous introduisez une table temporaire dans une requête, vous interrompez le flux de données via le processeur de requêtes.

Considérez la population d'une table temporaire comme un arrêt brutal, car il y a une requête (appelons-la le producteur) pour produire le jeu de résultats intermédiaire, qui est ensuite stocké dans la table temporaire de tempdb, puis la requête suivante (appelons-la le consommateur) doit relire les données de la table temporaire.

J'ai souvent constaté que certaines parties d'une charge de travail fonctionnent mieux lorsque la table temporaire est complètement supprimée, de sorte que les données circulent de la partie producteur de la requête vers la partie consommateur de la requête sans avoir à persister dans tempdb, et le l'optimiseur de requête peut produire un plan global plus optimal.

Vous pensez peut-être maintenant, "alors pourquoi quelqu'un utiliserait-il une table temporaire si cela rend les choses plus lentes?" – et à juste titre ! Dans des cas comme celui-là, j'ai constaté que l'utilisation d'une table temporaire s'est institutionnalisée dans l'équipe de développement; quelqu'un a découvert que l'utilisation d'une table temporaire augmentait les performances il y a de nombreuses années, de sorte que les tables temporaires sont devenues le choix de conception par défaut.

Cela peut être difficile à changer, surtout si vous avez un développeur senior ou un responsable convaincu que les tables temporaires doivent toujours être utilisées. La chose la plus simple à essayer est de choisir une requête coûteuse (par exemple, une requête longue ou exécutée plusieurs fois par seconde) et de supprimer une ou plusieurs des tables temporaires pour voir si les performances augmentent sans elles. Et si oui, en voilà votre preuve à montrer aux intransigeants !

Problème 2 :absence de filtrage lors du remplissage des tables temporaires

Même si vous ne pouvez pas supprimer une table temporaire, vous pourrez peut-être améliorer considérablement les performances en vous assurant que le code qui remplit la table temporaire filtre correctement les données extraites des tables source.

J'ai perdu le compte du nombre de fois où j'ai vu une table temporaire être remplie avec du code commençant par SELECT * , inclut quelques jointures non restrictives et n'a pas de clause WHERE, puis la dernière requête qui utilise la table temporaire n'utilise que quelques colonnes et a une clause WHERE pour réduire considérablement le nombre de lignes.

Je me souviens d'un cas où une table temporaire dans une procédure stockée agrégeait 15 ans de données de la base de données principale, puis seules les données de l'année en cours étaient utilisées. Cela provoquait à plusieurs reprises la croissance de tempdb jusqu'à ce qu'il manque d'espace sur le volume de disque, et la procédure stockée échouait alors.

Chaque fois que vous remplissez une table temporaire, n'utilisez que les colonnes de la table source qui sont nécessaires et n'utilisez que les lignes qui sont nécessaires - c'est-à-dire poussez les prédicats de filtre vers le haut dans le code de remplissage de la table temporaire. Non seulement cela permettra d'économiser de l'espace dans tempdb, mais cela permettra également de gagner beaucoup de temps en évitant de copier des données inutiles à partir de la table source (et en supprimant potentiellement le besoin de lire les pages de la base de données source à partir du disque en premier lieu).

Problème 3 :Indexation incorrecte de la table temporaire

Tout comme avec les tables normales, vous ne devez créer que les index qui seront réellement utilisés par le code de requête ultérieur pour améliorer les performances de la requête. J'ai vu de nombreux cas où il y a un index non clusterisé par colonne de table temporaire, et les index à une seule colonne qui sont choisis sans analyser le code ultérieur sont souvent assez inutiles. Combinez maintenant des index non clusterisés inutiles avec un manque de filtrage lors du remplissage de la table temporaire, et vous avez une recette pour un énorme gonflement de tempdb.

De plus, en général, il est plus rapide de créer les index une fois la table remplie. Cela donne l'avantage supplémentaire que les index auront des statistiques précises, ce qui peut encore aider la requête car l'optimiseur de requête sera en mesure d'effectuer une estimation précise de la cardinalité.

Avoir un tas d'index non clusterisés qui ne sont pas utilisés gaspille non seulement de l'espace disque, mais aussi le temps nécessaire pour les créer. S'il s'agit d'un code fréquemment exécuté, la suppression de ces index inutiles qui sont créés à chaque exécution du code peut avoir un effet significatif sur les performances globales.

Problème 4 :Conflit de verrouillage tempdb

Il est assez courant qu'il y ait un goulot d'étranglement de verrouillage dans tempdb qui peut être attribué à l'utilisation de la table temporaire. S'il existe de nombreuses connexions simultanées exécutant du code qui crée et supprime des tables temporaires, l'accès aux bitmaps d'allocation de la base de données en mémoire peut devenir un goulot d'étranglement important.

En effet, un seul thread à la fois peut modifier un bitmap d'allocation pour marquer les pages (de la table temporaire) comme allouées ou désallouées, et donc tous les autres threads doivent attendre, ce qui diminue le débit de la charge de travail. Même s'il existe un cache de table temporaire depuis SQL Server 2005, il n'est pas très volumineux et il existe des restrictions quant au moment où la table temporaire peut être mise en cache (par exemple, uniquement lorsqu'elle fait moins de 8 Mo).

Les méthodes traditionnelles pour contourner ce problème consistaient à utiliser l'indicateur de trace 1118 et plusieurs fichiers de données tempdb (voir cet article de blog pour plus d'informations), mais une autre chose à considérer est de supprimer complètement les tables temporaires !

Résumé

Les tables temporaires peuvent être très utiles, mais elles sont très facilement et couramment utilisées de manière incorrecte. Chaque fois que vous écrivez (ou révisez du code) qui utilise une table temporaire, considérez ce qui suit :

  • Cette table temporaire est-elle vraiment nécessaire ?
  • Le code qui remplit le tableau utilise-t-il le bon filtrage limiter la taille de la table temporaire ?
  • Les index sont-ils créés après le remplissage de la table ? (en général) et sont les index utilisés par un code ultérieur ?

Paul White a quelques articles intéressants (ici et ici) sur l'utilisation et la mise en cache des objets temporaires que je recommande également de lire.

Et une dernière chose, si vous décidez de ne pas utiliser une table temporaire, ne la remplacez pas simplement par une variable de table, une expression de table commune ou un curseur (qui sont tous des moyens courants utilisés par les gens pour "optimiser" le table temporaire) - trouvez la manière la plus efficace de (ré)écrire le code - il n'y a pas de réponse "taille unique".

Jusqu'à la prochaine fois, bon dépannage !