web-dev-qa-db-fra.com

De bonnes raisons d'utiliser SELECT ... WITH XLOCK?

Je fais face à des blocages récurrents, dont l'un est un verrou et contient une requête SELECT avec un indice XLOCK qui devient la victime du blocage. L'autre instruction est un INSERT dans l'une des tables qui fait partie de la vue de la première requête.

Vue:

create view dbo.viewE
 as
    select * from dbo.E  
    where myValue > 13000 

Sélectionnez la requête:

select * from dbo.viewE with (XLOCK) where A > GETUTCDATE() 

INSERT Statement:

INSERT INTO [dbo].[E] (myValue,A) VALUES (10,GetDate())

La table sous-jacente dbo.E contient environ 3 millions de lignes dans environ 20 colonnes, dont certaines sont ntext.

En retirant les requêtes et en les simulant manuellement avec deux transactions, le comportement est reproductible. Le comportement change si XLOCK est supprimé de la sélection.

Graphique de blocage:

<deadlock-list>
 <deadlock victim="process222222221">
  <process-list>
   <process id="process222222221" taskpriority="0" logused="0" waitresource="KEY: 5:72057604035644444 (ccdf51accc0c)" waittime="2522" ownerId="27202256401" transactionname="SELECT" lasttranstarted="2015-09-14T16:32:36.160" XDES="0x2f1ec5ca0" lockMode="RangeX-X" schedulerid="15" kpid="12936" status="suspended" spid="359" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2015-09-14T16:32:36.160" lastbatchcompleted="2015-09-14T16:32:36.160" clientapp="x" hostname="x" hostpid="14536" loginname="x" isolationlevel="serializable (4)" xactid="27202256401" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="48" sqlhandle="0x02000000611e4523142b2318c47c87313a9b2ba587ff3130">
        SELECT * FROM viewE WITH (XLOCK) WHERE A &lt; GetUtcDate()      </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@UICulture nvarchar(5))SELECT * FROM viewE WITH (XLOCK) WHERE A &lt; GetUtcDate()    </inputbuf>
   </process>
   <process id="process6022222" taskpriority="0" logused="161152" waitresource="KEY: 5:72057604035644444 (cd874c2ba438)" waittime="1370" ownerId="27202248438" transactionguid="0x8de5ccd6eeef67469c6234af59e44ca5" transactionname="DTCXact" lasttranstarted="2015-09-14T16:32:34.767" XDES="0x4aa0bf950" lockMode="RangeI-N" schedulerid="14" kpid="6636" status="suspended" spid="329" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2015-09-14T16:32:37.300" lastbatchcompleted="2015-09-14T16:32:37.300" clientapp="x" hostname="x" hostpid="14536" loginname="x" isolationlevel="read uncommitted (1)" xactid="27202248438" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="936" sqlhandle="0x020000004853462f09790a4ddedc0d574c2afa539aef1c0e">
     INSERT INTO [E] ([a], [b], [c],...) VALUES (@aDate, @bDate, @c, ...)
     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>INSERT INTO [E] ([a], [b], [c],...) VALUES (@aDate, @bDate, @c, ...)
    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <keylock hobtid="72057604035644444" dbid="5" objectname="db.dbo.E" indexname="IX_index1" id="lock258b6dc80" mode="X" associatedObjectId="72057604035644444">
    <owner-list>
     <owner id="process6022222" mode="X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process222222221" mode="RangeX-X" requestType="wait"/>
    </waiter-list>
   </keylock>
   <keylock hobtid="72057604035644444" dbid="5" objectname="db.dbo.E" indexname="IX_index1" id="lock7b145c400" mode="RangeX-X" associatedObjectId="72057604035644444">
    <owner-list>
     <owner id="process222222221" mode="RangeX-X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process6022222" mode="RangeI-N" requestType="wait"/>
    </waiter-list>
   </keylock>
  </resource-list>
 </deadlock>
</deadlock-list>

Pour autant que je comprends cela, je regarde un blocage KEYLOCK essentiellement provoqué par une requête d'index non couvert qui utilise un index non cluster et un cluster pour collecter les valeurs requises, non?

Mes questions:

  1. Je ne peux pas créer d'index de couverture en raison des colonnes NTEXT requises impliquées. Est-ce que la réduction drastique du nombre de lignes aidera ici?
  2. Y a-t-il une bonne raison pour laquelle je ne sais tout simplement pas que SELECT est exécuté avec XLOCK? Le blocage se produirait-il également sans XLOCK?
11
Magier

Pour autant que je comprends cela, je regarde un blocage KEYLOCK essentiellement provoqué par une requête d'index non couvert qui utilise un index non cluster et un cluster pour collecter les valeurs requises, non?

Essentiellement, oui. L'opération de lecture (sélection) accède d'abord à l'index non cluster, puis à l'index cluster (recherche). L'opération d'écriture (insertion) accède d'abord à l'index cluster, puis à l'index non cluster. L'accès aux mêmes ressources dans un ordre différent avec des verrous incompatibles peut entraîner un blocage.

Est-ce que la réduction drastique du nombre de lignes aidera ici?

Cela pourrait , car moins de ressources sont verrouillées et l'opération aura tendance à se terminer plus rapidement. Si cela aide, cela peut réduire les blocages, mais probablement ne pas les éliminer (mais continuer à lire).

Y a-t-il une bonne raison pour laquelle je ne sais tout simplement pas que SELECT est exécuté avec XLOCK?

Pas vraiment. Des conseils de verrouillage comme celui-ci sont souvent introduits par des personnes sans une parfaite compréhension du fonctionnement de l'isolement, du verrouillage et des blocages, dans une tentative désespérée de réduire ou d'éliminer un problème.

Le blocage se produirait-il également sans XLOCK?

Non, si la sélection s'exécute réellement à lire l'isolement non validé car les verrous incompatibles ne seront pas pris (et maintenus) dans un ordre différent.

Oui, si un niveau d'isolement de verrouillage est utilisé et que des verrous incompatibles sont pris et maintenus dans un ordre incohérent, par exemple partagé (S) sur le non cluster, puis S sur le cluster lors de la lecture. La probabilité d'un blocage dans ce scénario dépend du nombre de verrous pris et de leur durée.

Conseil

Ce qui ressort vraiment (lors de l'examen), c'est que la transaction de sélection s'exécute sous isolement sérialisable . Cela pourrait être défini par votre infrastructure ou en raison de l'utilisation du DTC (Distributed Transaction Coordinator) - voir transactionname = "DTCXact" dans le graphique de blocage. Vous devriez examiner les raisons de cela et chercher à le changer si possible.

Sans cette escalade vers sérialisable, les chances sont très bonnes que ce blocage ne se produise pas, en supposant que l'indice XLOCK est supprimé. Cela dit, vous liriez sous l'isolement de lecture non validée , qui comporte très peu de garanties de cohérence.

Si votre application et le code SQL Server peuvent tolérer la lecture de versions de lignes, passer à lire l'isolement de l'instantané validé (RCSI) ou l'isolement de l'instantané (SI) pour les lectures éviterait également le blocage (XLOCK supprimé! ), tout en présentant une vue cohérente et ponctuelle des données validées. Cela suppose également que vous pouvez éviter l'isolement sérialisable, bien sûr.

En fin de compte, l'indicateur XLOCK est contre-productif, mais vous devez vraiment rechercher la raison de l'utilisation du niveau d'isolement sérialisable . Le trancount = 2 est également intéressant - vous imbriquez peut-être involontairement des transactions ici. Autre chose à vérifier.

15
Paul White 9
  1. Une réduction drastique du nombre de lignes réduira la probabilité d'obtenir l'impasse, mais elle ne disparaîtra pas complètement.

En termes simples, la sélection utilise d'abord l'index pour déterminer les lignes à sélectionner, puis récupère les lignes, tandis que l'insertion insère une ligne, puis essaie de mettre à jour l'index (XLOCKED).

  1. Les développeurs d'applications ont tendance à utiliser XLOCK si, au cours de la même transaction, ils souhaitent effectuer ultérieurement une mise à jour des données. Cela garantit que personne ne peut mettre à jour les données sous eux. J'examinerais ce que l'application fait pour voir si le XLOCK est requis.

Cela dit, la suppression du XLOCK ne résoudra probablement pas le problème. SELECT sélectionnera toujours un verrou partagé sur l'index, et INSERT voudra qu'un XLOCK le mette à jour. Un verrou partagé et un XLOCK ne peuvent pas exister ensemble sur l'objet, vous obtiendrez donc toujours un blocage. IX_Index1 doit être MyValue ou A, ou les deux.

Ce type de blocage se produit souvent en raison d'index mal conçus et/ou d'un trop grand nombre d'index. Ou du code mal écrit. Votre meilleure option est de voir s'il existe un moyen de réécrire la sélection pour utiliser un autre index.

2
Leo Miller