L'indice Tablock déclenche des blocages

10

J'insérais deux ensembles de données, en utilisant une journalisation minimale, dans une table de tas vide à l'aide de deux tâches d'exécution SQL exécutées en parallèle et avec SQL de la forme suivante.

INSERT INTO Table (TABLOCK) SELECT FROM ...

Une fois le travail bloqué, l'une des tâches SQL est devenue une victime de blocage. Ci-dessous, la sortie XML du graphique de blocage.

Quelqu'un peut-il expliquer ce qui se passait sous le capot?

  <resource-list>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process9609dc8" mode="Sch-S"/>
     <owner id="process9609dc8" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process5e13048" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process5e13048" mode="Sch-S"/>
     <owner id="process5e13048" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process9609dc8" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
  </resource-list>

Les choses deviennent beaucoup plus délicates car j'ai trouvé que dans la plupart des cas, les deux tâches d'exécution SQL peuvent s'exécuter en parallèle avec succès. Essayez ci-dessous:

Create table dbo.TablockInsert (c1 int, c2 int, c3 int)

--then issue the script in two Execute Sql Task in parallel you won't fail:
insert into dbo.TablockInsert(TABLOCK) SELECT 1, 1, 1

Étant donné que la seule différence est l'instruction SELECT ... FROM ..., ressemble à l'instruction SELECT ... FROM ... peut avoir un impact sur le mode de verrouillage ici?

SqlWhale
la source
Vous pouvez spécifier TABLOCKX au lieu de TABLOCK pour éviter le blocage. Bien que cela sérialise également l'accès à la table, vous obtiendrez toujours une journalisation minimale.
Dan Guzman

Réponses:

8

Le Guide de performances de chargement des données a été écrit pour SQL Server 2008, mais pour autant que je sache, Microsoft n'a apporté aucune amélioration dans ce domaine pour les tas. Voici un devis pour votre scénario de chargement:

Chargement en bloc d'une table vide et non partitionnée

Le chargement de données dans une table non partitionnée, tout en étant une opération simple, peut être optimisé de plusieurs manières.

...

Plusieurs opérations d'insertion simultanées pour les tas ne sont possibles que lorsque la méthode en bloc choisie émet des verrous de mise à jour en bloc (BU) sur la table. Deux verrous de mise à jour en bloc (BU) sont compatibles, et donc deux opérations en bloc peuvent s'exécuter en même temps.

Dans ce scénario, INSERT… SELECT et SELECT INTO ont tous deux un inconvénient. Ces deux opérations prennent un verrou de niveau table exclusif (X) sur la destination. Cela signifie qu'une seule opération de chargement en bloc peut s'exécuter à un moment donné, ce qui limite l'évolutivité. Cependant, BCP, BULK INSERT et Integration Services sont tous capables de prendre des verrous de mise à jour en bloc (BU) - si vous spécifiez le conseil TABLOCK.

La partie importante est que vous n'obtenez pas de verrou BU avec INSERT ... SELECT. Vous obtiendrez toujours un verrou exclusif sur la table, donc un seul INSERTpeut fonctionner à la fois.

Dans les commentaires, vous avez dit que vous inséreriez 100 000 lignes ou moins et que d'autres processus ne s'exécuteraient pas sur les tables lors des insertions. Lors de l'envoi de deux requêtes INSERT à la base de données, je m'attendrais à ce qu'une des trois choses se produise:

  1. Un insert s'exécute en premier et bloque l'autre insert. Le deuxième insert attend que le premier insert soit terminé.
  2. Un insert se termine avant le deuxième insert. Il n'y a pas de blocage explicite mais ils ne sont pas exécutés simultanément.
  3. Vous obtenez un blocage et une seule insertion réussit.

Dans tous les cas, vous bénéficiez ou n'êtes pas blessé en ajoutant un TABLOCKXindice à la requête, c'est donc ma recommandation de contourner l'impasse. Si vous voulez savoir pourquoi l'impasse se produit parfois, vous devrez chercher une autre réponse pour cela.

Dans un scénario différent dans lequel vous avez vraiment besoin d'une insertion parallèle, deux façons de contourner le problème BU sont de partitionner votre segment et d'avoir chaque session insérée dans une partition distincte ou de charger vos données via BCP, BULK INSERT ou Integration Services .

Joe Obbish
la source
Merci pour la réponse, mais la situation dans laquelle j'ai rencontré une impasse est le seul cas que j'ai maintenant. Dans la plupart des cas, lorsque vous lancez INSERT INTO WITH (TABLOCK) SELECT FROM en parallèle, le travail n'échouera pas. BTW, j'utilise SQL SERVER 2008 R2. J'ai ajouté un exemple réussi dans ma question.
SqlWhale
@tec dans les cas où il n'échoue pas, ils finissent probablement par fonctionner en série. Peut-être que vous ne rencontrez le problème que lorsqu'il y a un certain temps de démarrage, par exemple pour la compilation du plan.
Martin Smith
@MartinSmith La requête où j'ai rencontré le problème sur le projet est beaucoup plus complexe que l'exemple SELECT 1, qui nécessite plus de surcharge de compilation mais il ne fait que lire à partir de quelques tables. J'essaie de reproduire ce type de blocage par des requêtes plus complexes.
SqlWhale
@TecKnowNothing Environ combien de lignes insérez-vous? Combien de fois par jour le processus s'exécute-t-il? D'autres requêtes SELECTIONNENT-elles dans la table pendant le chargement des données?
Joe Obbish
@JoeObbish 1. Je ne pense pas que le nombre de lignes soit en cause ici, nous n'avons que 4000 à 70000 pour chacune des requêtes et ce sont des données hautement agrégées pour une utilisation de cube. 2. Il est toujours en phase de test, censé fonctionner une fois par jour. 3. Rien d'autre ne lit dans la table cible.
SqlWhale
4

Vous vous insérez à dbo.TargetTablepartir de deux sessions et les deux en utilisant TABLOCKhint.Both process9609dc8et process5e13048process holding Sch-Set IXverrous qui sont compatibles les uns avec les autres afin que les deux processus puissent tenir en même temps. Mais les deux veulent convertir le IXverrou en Exclusive Xtype. Xles serrures ne sont pas compatibles entre elles. Par conséquent, SQL Server a choisi l'une des sessions comme victime de blocage au lieu d'attendre indéfiniment.

Informations de base sur l'impasse.

Tableau de compatibilité des verrous (moteur de base de données).

Détection et suppression des interblocages.

SqlWorldWide
la source