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

Statistiques d'attente épidermiques :PAGELATCH

Au cours des 18 derniers mois, je me suis concentré sur les réactions instinctives à l'analyse des statistiques d'attente et sur d'autres sujets liés à l'optimisation des performances. Dans cet article, je vais continuer sur ce sujet et discuter du PAGELATCH_XX attend. Le XX à la fin de l'attente signifie qu'il existe plusieurs types de PAGELATCH attendez, et les exemples les plus courants sont :

  • PAGELATCH_SH – ( SH sont) en attente d'accès à une page de fichier de données en mémoire pour que le contenu de la page puisse être lu
  • PAGELATCH_EX ou PAGELATCH_UP – (EX exclusif ou UP date) en attente d'accès à une page de fichier de données en mémoire pour modification du contenu de la page

Lorsque l'un de ces types d'attente est le plus répandu sur un serveur, la réaction instinctive est que le problème est quelque chose à voir avec les E/S (c'est-à-dire une confusion avec le PAGEIOLATCH_XX type d'attente, que j'ai couvert dans un article en 2014) et quelqu'un essaie d'ajouter plus de mémoire ou de peaufiner le sous-système d'E/S. Aucune de ces réactions n'aura d'effet, car les pages de fichiers de données en conflit sont déjà en mémoire dans le pool de mémoire tampon !

Dans tous les cas, vous pouvez voir si vous avez un problème avec PAGELATCH_XX conflit à l'aide de sys.dm_os_waiting_tasks script sur mon blog ou en utilisant un outil comme Performance Advisor, comme démontré (pour un type d'attente différent) dans cet article.

Alors, quelle est la source de la dispute? J'expliquerai d'abord le contexte derrière ces types d'attente, puis j'aborderai les deux causes les plus courantes de PAGELATCH_XX conflit.

Contexte :Loquets

Avant d'aborder certaines des causes de PAGELATCH_XX attends, je veux expliquer pourquoi ils existent même.

Dans tout système multi-thread, les structures de données accessibles et manipulables par plusieurs threads doivent être protégées pour éviter des scénarios tels que :

  • Deux threads mettent à jour une structure de données simultanément, et certaines des mises à jour sont perdues
  • Un thread met à jour une structure de données en même temps qu'un autre thread lit la structure de données, de sorte que le thread de lecture voit un mélange d'anciennes et de nouvelles données

C'est de l'informatique de base, et SQL Server n'est pas différent, donc toutes les structures de données à l'intérieur de SQL Server doivent avoir un contrôle d'accès multithread.

L'un des mécanismes que SQL Server utilise pour ce faire s'appelle un verrou, où le maintien du verrou en mode exclusif empêche les autres threads d'accéder à la structure de données, et le maintien du verrou en mode partagé empêche les autres threads de modifier la structure des données. SQL Server utilise également des spinlocks pour certaines structures de données et j'en ai parlé dans cet article en 2014.

Mais pourquoi une page de fichier de données en mémoire est-elle protégée par un loquet, vous vous demandez peut-être ? Eh bien, une page de fichier de données n'est qu'une structure de données, même si elle a un but particulier, et nécessite donc les mêmes contrôles d'accès que toute autre structure de données. Ainsi, lorsqu'un thread doit modifier une page de fichier de données, il doit acquérir un verrou exclusif ou de mise à jour sur la page, et s'il ne peut pas et doit attendre, le type d'attente PAGELATCH_EX ou PAGELATCH_UP résultats.

Conflit tempdb classique

PAGELATCH les conflits dans tempdb concernent généralement les bitmaps d'allocation et se produisent avec des charges de travail avec de nombreuses connexions simultanées créant et supprimant de petites tables temporaires (qui sont stockées dans tempdb).

Lorsque la première ligne est insérée dans une table temporaire, deux pages doivent être allouées (une page de données et une page IAM, qui suit la page de données). Ces pages doivent être marquées comme allouées dans une page d'allocation spéciale appelée page PFS, et par défaut sont allouées à partir d'étendues de données spéciales qui sont suivies par une autre page d'allocation appelée page SGAM (les détails de ceux-ci peuvent être trouvés dans mon ancien article de blog ici). Lorsque la table temporaire est supprimée, ces pages doivent à nouveau être désallouées, ce qui nécessite d'autres modifications des pages PFS et SGAM.

Si les tables temporaires sont petites et que la taille cumulée de toutes les tables temporaires créées simultanément est inférieure à 64 Mo, toutes ces modifications de bitmap d'allocation sont centrées sur les toutes premières pages PFS et SGAM du fichier de données tempdb (avec l'ID de page (1 :1) et (1:3) respectivement). La mise à jour de l'une de ces pages d'allocation nécessite de verrouiller la page, et un seul thread à la fois peut changer de page, donc tous les autres threads doivent attendre - avec le type d'attente PAGELATCH_UP .

À partir de SQL Server 2005, les tables temporaires peuvent être mises en cache lorsqu'elles sont supprimées, tant qu'elles ont une taille inférieure à 8 Mo (et dans SQL Server 2014, elles ne sont pas créées dans une procédure stockée qui contient également des instructions DDL sur la table temporaire). Cela signifie que le thread suivant qui exécute le même plan de requête peut retirer la table temporaire du cache et ne pas avoir à gérer les allocations initiales. Cela réduit les conflits sur les bitmaps d'allocation, mais le cache de la table temporaire n'est pas très volumineux, de sorte que les charges de travail avec des centaines de créations/suppressions simultanées de tables temporaires connaîtront toujours de nombreux conflits.

Il est trivial d'empêcher la contention sur les pages SGAM dans tempdb en activant l'indicateur de trace documenté 1118 sur le serveur, qui, selon moi, devrait être activé sur tous les serveurs à travers le monde, et est en fait le comportement par défaut immuable dans SQL Server 2016.

La prévention des conflits sur les pages PFS dans tempdb est un peu plus difficile. En supposant que les tables temporaires sont nécessaires pour les performances, l'astuce consiste à disposer de plusieurs fichiers de données pour tempdb afin que les allocations soient effectuées à tour de rôle entre les fichiers, la contention est répartie sur plusieurs pages PFS, et ainsi la contention globale diminue. Il n'y a pas de bonne réponse pour combien de fichiers de données vous devriez avoir malheureusement. Vous pouvez en savoir plus sur les conseils généralement acceptés à ce sujet dans l'article 2154845 de la base de connaissances et dans ce billet de blog.

Insérer un point d'accès

Dans les bases de données utilisateur, une cause fréquente du nombre élevé de PAGELATCH_EX waits est un hotspot d'insertion.

Cela peut se produire lorsqu'une table a un index clusterisé avec une clé de cluster int ou bigint et une taille de ligne suffisamment petite pour que plusieurs dizaines de lignes de table ou plus puissent tenir sur une page de données au niveau feuille de l'index clusterisé.

Pour une telle table, si la charge de travail implique plusieurs dizaines ou centaines de threads simultanés insérés dans la table, de nombreux threads généreront des lignes avec des valeurs d'identité (et donc des clés de cluster) qui doivent être insérées sur la même page de données de niveau feuille .

Rappelez-vous maintenant que toute modification d'une page de fichier de données en mémoire nécessite un verrou exclusif, de sorte que chacun des threads essayant de s'insérer sur la même page doit acquérir exclusivement le verrou de la page. Pendant que chaque thread maintient le verrou exclusif, les autres threads attendent PAGELATCH_EX pour cette page, transformant essentiellement les insertions simultanées en un processus synchrone extrêmement goulot d'étranglement.

Il existe quelques solutions possibles à ce problème :

  • Utilisez une clé plus aléatoire et reconnaissez que cela conduira à la fragmentation de l'index. Utilisez donc également un facteur de remplissage de l'index pour éviter les fractionnements de page.
  • Répartir les insertions dans la table à l'aide d'une sorte de mécanisme de partitionnement artificiel
  • Utilisez une taille de ligne de tableau plus longue (il s'agit évidemment de l'option la moins acceptable)

J'ai vu un hotspot d'insertion comme celui-ci apparaître lorsque quelqu'un a essayé de supprimer les problèmes de fragmentation d'index en remplaçant une clé de cluster GUID aléatoire par une clé de cluster d'identité int ou bigint, mais n'a pas réussi à tester le nouveau schéma de table sous des charges de production.

Résumé

Comme pour les autres types d'attente, comprendre exactement ce que PAGELATCH_XX signifie que les attentes sont essentielles pour comprendre comment les résoudre.

En ce qui concerne les statistiques d'attente générales, vous pouvez trouver plus d'informations sur leur utilisation pour le dépannage des performances dans :

  • Ma série d'articles de blog SQLskills, en commençant par les statistiques d'attente, ou dites-moi où ça fait mal ;
  • Bibliothèque de mes types d'attente et de mes classes de verrouillage ici
  • Ma formation en ligne Pluralsight SQL Server :Dépannage des performances à l'aide des statistiques d'attente
  • Conseiller de performances SQL Sentry

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