web-dev-qa-db-fra.com

Pourquoi suis-je des blocages avec 1 lecture et 1 fil d'écriture dans SQL

si quelqu'un pouvait m'aider à comprendre pourquoi l'impasse Transactions suivantes? Je fournis les 2 transactions et les index de chaque table:

Transaction 1:

SELECT blockId, blockData, syncedToGeneration, itemId 
FROM blocks 
WHERE indexId=@indexId 
and itemId IN (SELECT itemId 
               FROM KeywordReferences 
               WHERE indexId=@indexId 
               AND keywordRootId IN (360,4498,359,1229))

Transaction 2:

UPDATE blocks 
   SET blockData=@blockData,
       syncedToGeneration=@syncedToGeneration 
   WHERE indexId=@indexId 
AND blockId=@blockId

(Veuillez noter que la section "in" dans la première transaction est beaucoup plus longue et contient environ 30 valeurs que j'ai tronquées pour des raisons de lisibilité)

La table des blocs contient les index suivants:
[.____] - IndexId-> BlockID (groupé)
[.____] - IndexId-> ItemID

indexid-> itemID

La table KeyWordReferences a les index suivants:
[.____] - IndexId_> motwordrotitid (groupé)
[.____] - indexID-> motwordrootid-> score
[.____] - IndexId-> ItemID
[.____] - IndexId-> BlockID

BOWELLE est la sortie XML graphique de l'impasse:

<deadlock-list>
 <deadlock victim="process5a274c8">
  <process-list>
   <process id="process5a274c8" taskpriority="0" logused="0" waitresource="PAGE: 5:1:91671" waittime="2709" ownerId="122348" transactionname="SELECT" lasttranstarted="2012-04-19T21:23:26.680" XDES="0xf382aca0" lockMode="S" schedulerid="4" kpid="10992" status="suspended" spid="69" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2012-04-19T21:23:26.650" lastbatchcompleted="2012-04-19T21:23:26.647" clientapp=".Net SqlClient Data Provider" hostname="AMIT-PC" hostpid="6752" loginname="sa" isolationlevel="read committed (2)" xactid="122348" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="34" sqlhandle="0x020000002e9a3633b816ffc89dc234b4c0351887e4e1b2cf">
SELECT blockId, blockData, syncedToGeneration, itemId FROM blocks WHERE indexId=@indexId and itemId IN (SELECT itemId FROM KeywordReferences WHERE indexId=@indexId AND keywordRootId IN (360,4498,359,1229,2143,14330,7661,3755,1156,21490,5567,1933,429,28197,2,3165,524,3182,2655,27262,17407,2673,570,1478,3802,6838,19668,17,6586,2484,2794,1640,5171,2558,6592,5833,695,1199,2307,335,1351,6651,6899,3740,7048,22030,14356,597,3175,3965,3297,2711,14484,2761,2265,28,1647,3223,226,304,298,1157,197,2696,21172,19149,9,1159,135,1,3166,23325))     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@indexId bigint)SELECT blockId, blockData, syncedToGeneration, itemId FROM blocks WHERE indexId=@indexId and itemId IN (SELECT itemId FROM KeywordReferences WHERE indexId=@indexId AND keywordRootId IN (360,4498,359,1229,2143,14330,7661,3755,1156,21490,5567,1933,429,28197,2,3165,524,3182,2655,27262,17407,2673,570,1478,3802,6838,19668,17,6586,2484,2794,1640,5171,2558,6592,5833,695,1199,2307,335,1351,6651,6899,3740,7048,22030,14356,597,3175,3965,3297,2711,14484,2761,2265,28,1647,3223,226,304,298,1157,197,2696,21172,19149,9,1159,135,1,3166,23325))    </inputbuf>
   </process>
   <process id="process5a13b88" taskpriority="0" logused="215304" waitresource="PAGE: 5:1:91669" waittime="2910" ownerId="128212" transactionname="user_transaction" lasttranstarted="2012-04-19T21:23:28.567" XDES="0xedcd9000" lockMode="IX" schedulerid="2" kpid="5500" status="suspended" spid="68" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2012-04-19T21:23:29.007" lastbatchcompleted="2012-04-19T21:23:29.007" clientapp=".Net SqlClient Data Provider" hostname="AMIT-PC" hostpid="6752" loginname="sa" isolationlevel="read committed (2)" xactid="128212" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="154" sqlhandle="0x02000000f4d83b0df2bfedc5a346288c21fa78e07eb152f6">
UPDATE blocks SET blockData=@blockData, syncedToGeneration=@syncedToGeneration WHERE indexId=@indexId AND blockId=@blockId     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@indexId bigint,@blockId int,@blockData ntext,@syncedToGeneration int)UPDATE blocks SET blockData=@blockData, syncedToGeneration=@syncedToGeneration WHERE indexId=@indexId AND blockId=@blockId    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <pagelock fileid="1" pageid="91671" dbid="5" objectname="isqdb.dbo.blocks" id="lock5c54700" mode="IX" associatedObjectId="72057594043826176">
    <owner-list>
     <owner id="process5a13b88" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process5a274c8" mode="S" requestType="wait"/>
    </waiter-list>
   </pagelock>
   <pagelock fileid="1" pageid="91669" dbid="5" objectname="isqdb.dbo.blocks" id="lock5b84780" mode="S" associatedObjectId="72057594043826176">
    <owner-list>
     <owner id="process5a274c8" mode="S"/>
    </owner-list>
    <waiter-list>
     <waiter id="process5a13b88" mode="IX" requestType="wait"/>
    </waiter-list>
   </pagelock>
  </resource-list>
 </deadlock>
</deadlock-list>
4
Amit Ben Shahar

Cela ressemble à un cas typique de balayage de table (index manquants possibles, mais pourrait être plus). Le SELECT a choisi une granularité de verrouillage de la page, indicatif d'une grande analyse (sur blocks tableau). Notez également comment tous les verrouillages sont sur la même source de ressources, l'index en cluster, une autre indication que le SELECT n'utilise pas d'index secondaire sélectif pour localiser les rangées. Vous semblez avoir espéré que l'indice non plané sur (indexId, itemId), que vous décrivez comme existant, cela aurait été choisi et peut-être, avait-il été choisi, la requête ne serait pas une impasse. Comme les choses se tiendront, on dirait que cela est ignoré le plus probable à cause de la sélectivité (il frappe le point de basculement ). Est difficile à conseiller pour un meilleur index W/o connaissant votre charge de travail et votre modèle de données. À tout le moins, je peux dire que l'exigence de projeter blockData, syncedToGeneration Dans la liste Select, l'index ne couvre pas, alors peut-être peut-être comme des colonnes incluses constitueraient une première étape. Mais w/o connaissant la taille de ces colonnes (blockData semble être le nom d'un grand ...) est encore difficile à prédire le résultat.

Pour illustrer pourquoi la numérisation est le coupable, tenez compte des deux lignes mises à jour par la déclaration de mise à jour. La mise à jour les mettra à jour dans l'ordre (indexId, blockId) Mais la numérisation sélectionnée les verra dans la commande (indexId, itemId). Sauf si il y a une dépendance fonctionnelle entre itemId et blockId qui garantit que les deux clés d'index rendent toutes les lignes de la même commande, il y aura toujours une paire de lignes pour lesquelles la commande est inversée entre l'indice en cluster et l'indice non groupé. L'essentiel est que, comme le SELECT fait une analyse, il est garanti de visiter chaque Row. Par conséquent Tout La paire de lignes mises à jour par la mise à jour sera visitée par l'analyse. Il y a quatre possibilités:

  • les deux lignes mises à jour sont derrière (dans l'ordre de clé d'index en cluster), la position actuelle de la balayage (la ligne actuelle étant numérisée). Ceci est sûr, les blocs de mise à jour et attend la numérisation.
  • les deux rangées mises à jour sont en avance sur la position de la numérisation. Aussi sans danger, la numérisation bloque et attend la mise à jour.
  • la première ligne mise à jour est derrière la position actuelle de la balayage, mais la seconde est à l'avance. Sûr, les blocs de mise à jour.
  • la première ligne mise à jour est en avance sur la position actuelle de l'analyse, mais la seconde est en retard. Ceci est une impasse garantie . Notez que l'Appart supplémentaire que les lignes sont dans l'ordre d'index en cluster, plus la probabilité d'un être à l'avance et une étant derrière tout position de balayage.

Cette situation arrête toujours avec des mises à jour de plusieurs lignes en présence de scans. La solution consiste généralement à remplacer le balayage par une opération plus sélective, qui a l'avantage supplémentaire d'améliorer les performances. Si la sélection et la mise à jour ne visitent que les lignes, ils désirent vraiment traiter, puis une impasse ne peut se produire que si deux opérations se produisent simultanément sur les mêmes données logiques (c.-à-d. Le même article est lu et mis à jour en même temps) mais la plupart des temps. sur OLTP Systems Ceci est empêché par le flux de travail de l'entreprise. Cela peut toujours arriver, mais avec une fréquence beaucoup réduite. En tant que note latérale, même lorsqu'il fonctionne sur des articles logiques disjoints, il est toujours n jeu de probabilités en raison de collisions de hachage .

Un moyen facile de sortir est, en effet, le déploiement isolement d'instantané . Mais le problème sous-jacent (le scan) ne sera masqué que masqué, non allégé. D'autres problèmes découlant du scan (performances médiocres, de la latence élevée et du temps de réponse lente, la pollution de la piscine tampon, etc.) vont être présents. Vous devez corriger le problème de numérisation et Déployer l'isolement d'instantané.

P.s. Notez que le fait que la numérisation utilise les serrures de granularité de la page n'a aucune pertinence. Si la balayage avait l'utilisation de la granularité de la rangée, l'impasse se produirait simplement sur des lignes au lieu des pages. Mais le fait que la granularité de page ait choisi est significative comme une révélation pour une analyse.

8
Remus Rusanu

On dirait que ceci est une explication possible:

  1. Le SELECT conserve un verrou partagé sur un index non en cluster sur l'élémentId, attend d'acquérir un autre verrou partagé sur l'index en cluster, de sorte qu'il puisse récupérer plus de colonnes.

  2. La mise à jour a modifié une page de l'index en cluster et conserve bien un verrou exclusif. Il attend également modifier l'indice non clustered.

Pouvez-vous poster le DDL pour les tables et index impliqués?

3
A-K

Je pense que l'impasse est due au moteur à acquérir des serrures de rangée sur chacune de ces valeurs d'élémentId, l'une à l'autre. Chaque requête fonctionne sur une liste non traitée des valeurs de l'ITPID et acquiert ces serrures dans un ordre différent, ce qui leur permet de se mêler à une intersectionnelle et, éventuellement, chaque requête se déroule avec une serrure que l'autre requête a déjà et a une serrure que l'autre requête veuille. L'autre chose est que SQL essaie d'essayer de promouvoir les serrures à une serrure de table. Définir le niveau d'isolation de la transaction Lire non engagée sur la connexion qui sélectionne peut vous aider, ou vous pouvez vous débarrasser de l'impasse par Dossier la granularité de verrouillage à la page ou même à la table.

2
darin strait