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

Le loquet APPEND_ONLY_STORAGE_INSERT_POINT

Poursuivant ma série d'articles sur les verrous, cette fois, je vais discuter du verrou APPEND_ONLY_STORAGE_INSERT_POINT et montrer comment il peut être un goulot d'étranglement majeur pour les charges de travail de mise à jour lourdes où l'une ou l'autre forme d'isolement d'instantané est utilisée.

Je vous recommande fortement de lire le premier article de la série avant celui-ci, afin que vous ayez toutes les connaissances générales sur les verrous.

Qu'est-ce que le loquet APPEND_ONLY_STORAGE_INSERT_POINT ?

Pour expliquer ce verrou, je dois expliquer un peu comment fonctionne l'isolement d'instantané.

Lorsque vous activez l'une des deux formes de versioning, SQL Server utilise un mécanisme appelé versioning pour conserver les versions avant modification d'un enregistrement dans le magasin de versions dans tempdb. Cela se fait comme suit :

  • Un enregistrement est identifié comme étant sur le point d'être modifié.
  • L'enregistrement actuel est copié dans le magasin de versions.
  • L'enregistrement est modifié.
  • Si l'enregistrement n'avait pas déjà une balise de versionnage de 14 octets , un est ajouté à la fin de l'enregistrement. La balise contient un horodatage (pas une heure réelle) et un pointeur vers la version précédente de l'enregistrement dans le magasin de versions.
  • Si l'enregistrement avait déjà une balise de version, il est mis à jour avec le nouvel horodatage et le pointeur de magasin de version.

L'horodatage de version à l'échelle de l'instance est incrémenté chaque fois qu'une nouvelle instruction ou un nouveau lot commence, ou qu'une nouvelle version d'un enregistrement est créée, dans toute base de données où l'une ou l'autre forme d'isolement d'instantané est activée. Cet horodatage est utilisé pour s'assurer qu'une requête traite la version correcte d'un enregistrement.

Par exemple, imaginez une base de données pour laquelle l'instantané validé en lecture est activé, de sorte que chaque instruction est garantie de voir les enregistrements au moment où l'instruction a commencé. L'horodatage de version est défini pour le démarrage de l'instruction, de sorte que tout enregistrement rencontré qui a un horodatage plus élevé est la "mauvaise" version, et donc la "bonne" version, avec un horodatage avant l'horodatage de l'instruction, doit être récupérée à partir du magasin de versions. Les mécanismes de ce processus ne sont pas pertinents pour les besoins de cet article.

Alors, comment les versions sont-elles stockées physiquement dans le magasin de versions ? L'ensemble de l'enregistrement avant modification, y compris les colonnes hors ligne, est copié dans le magasin de versions, divisé en blocs de 8 000 octets, qui peuvent s'étendre sur deux pages si nécessaire (par exemple, 2 000 octets à la fin d'une page et 6 000 octets à le début de la suivante). Ce stockage à usage spécial est composé d'unités d'allocation en ajout uniquement et n'est utilisé que pour les opérations de magasin de versions. On l'appelle ainsi parce que les nouvelles données ne peuvent être ajoutées qu'immédiatement après la fin de la dernière version saisie. Une nouvelle unité d'allocation est créée de temps en temps, ce qui permet au nettoyage régulier du magasin de versions d'être très efficace, car une unité d'allocation inutile peut simplement être supprimée. Encore une fois, les mécanismes de cela sortent du cadre de cet article.

Et maintenant, nous arrivons à la définition du verrou :tout thread qui a besoin de copier un enregistrement avant modification dans le magasin de versions doit savoir où se trouve le point d'insertion dans l'unité d'allocation en ajout uniquement actuelle. Ces informations sont protégées par le loquet APPEND_ONLY_STORAGE_INSERT_POINT.

Comment le loquet devient-il un goulot d'étranglement ?

Voici le problème :il n'y a qu'un seul mode acceptable dans lequel le verrou APPEND_ONLY_STORAGE_INSERT_POINT peut être acquis :le mode EX (exclusif). Et comme vous le saurez en lisant l'article d'introduction de la série, un seul fil à la fois peut maintenir le loquet en mode EX.

Rassembler toutes ces informations :lorsqu'une ou plusieurs bases de données ont une isolation d'instantané activée, et qu'il y a une charge de travail simultanée suffisamment élevée de mises à jour de ces bases de données, il y aura beaucoup de versions générées par les différentes connexions, et ce verrou deviendra un un peu un goulot d'étranglement, la taille du goulot d'étranglement augmentant à mesure que la charge de travail de mise à jour augmente lorsque la gestion des versions est impliquée.

Montrer le goulot d'étranglement

Vous pouvez facilement reproduire le goulot d'étranglement par vous-même. Je l'ai fait comme suit :

  • Création d'une table avec un groupe de colonnes entières nommées cXXX où XXX est un nombre et un index clusterisé sur une colonne d'identité int nommée DocID
  • 100 000 enregistrements insérés, avec des valeurs aléatoires pour toutes les colonnes
  • Création d'un script avec une boucle infinie pour sélectionner un DocID aléatoire dans la plage de 1 à 10 000, sélectionner un nom de colonne aléatoire et incrémenter la valeur de la colonne de 1 (créant ainsi une version)
  • Création de neuf scripts identiques, mais chacun sélectionnant une plage de clés de cluster différente de 10 000 valeurs
  • Définissez DELAYED_DURABILITY sur FORCED pour réduire les attentes WRITELOG (vous le feriez certes rarement, mais cela contribue à aggraver le goulot d'étranglement à des fins de démonstration)

J'ai ensuite exécuté les dix scripts simultanément et mesuré le compteur Méthodes d'accès :Recherches d'index/sec pour suivre le nombre de mises à jour. Je ne pouvais pas utiliser Databases:Batch Requests/sec car chaque script n'avait qu'un seul lot (la boucle infinie), et je ne voulais pas utiliser Transactions/sec car cela pourrait compter les transactions internes ainsi que celle qui enveloppe chaque mise à jour.

Lorsque l'isolation d'instantané n'était pas activée, sur mon ordinateur portable Windows 10 exécutant SQL Server 2019, j'obtenais environ 80 000 mises à jour par seconde sur les dix connexions. Ensuite, lorsque j'ai activé le paramètre READ_COMMMITED_SNAPSHOT pour la base de données et que j'ai relancé le test, le débit de la charge de travail est tombé à environ 60 000 mises à jour par seconde (une baisse de 25 % du débit). D'après les statistiques d'attente, 85 % de toutes les attentes étaient LATCH_EX, et d'après les statistiques de verrouillage, 100 % concernaient APPEND_ONLY_STORAGE_INSERT_POINT.

Gardez à l'esprit que j'ai mis en place le scénario pour montrer le goulot d'étranglement à son pire. Dans un environnement réel avec une charge de travail mixte, les recommandations généralement acceptées pour une baisse de débit lors de l'utilisation de l'isolement d'instantané sont de 10 à 15 %.

Résumé

Un autre domaine potentiel qui pourrait être affecté par ce goulot d'étranglement est les secondaires lisibles du groupe de disponibilité. Si un réplica de base de données est défini pour être lisible, toutes les requêtes sur celui-ci utilisent automatiquement l'isolement d'instantané, et toute relecture des enregistrements de journal à partir du principal générera des versions. Avec une charge de travail de mise à jour suffisamment élevée provenant du primaire et de nombreuses bases de données définies pour être lisibles, et avec la répétition parallèle étant la norme pour les secondaires de groupe de disponibilité, le verrou APPEND_ONLY_STORAGE_INSERT_POINT peut également devenir un goulot d'étranglement sur un secondaire lisible de groupe de disponibilité, ce qui pourrait conduire à la secondaire en retard sur le primaire. Je n'ai pas testé cela, mais c'est exactement le même mécanisme que j'ai décrit ci-dessus, donc cela semble probable. Dans ce cas, il est possible de désactiver la répétition parallèle à l'aide de l'indicateur de trace 3459, mais cela pourrait entraîner une dégradation du débit global sur le secondaire.

Mettre de côté le scénario de groupe de disponibilité, malheureusement, ne pas utiliser l'isolement d'instantané est le seul moyen d'éviter complètement ce goulot d'étranglement, ce qui n'est pas une option viable si votre charge de travail repose sur la sémantique fournie par l'isolement d'instantané, ou si vous en avez besoin pour atténuer les problèmes de blocage. (car l'isolement d'instantané signifie que les requêtes de lecture n'acquièrent pas de verrous de partage qui bloquent les requêtes de modification).

Modifier :à partir des commentaires ci-dessous, vous * pouvez * supprimer le goulot d'étranglement du verrou en utilisant ADR dans SQL Server 2019, mais les performances sont alors bien pires en raison de la surcharge ADR. Le scénario dans lequel le verrou devient un goulot d'étranglement en raison de la charge de travail élevée de mise à jour n'est absolument pas un cas d'utilisation valide pour l'ADR.