2011-03-28 3 views
6

Je réalise actuellement des expériences sur une base de données SQL Server 2008. Plus précisément, j'ai une application JDBC qui utilise des centaines de threads simultanés pour exécuter des milliers de tâches, dont chacun exécute la requête suivante sur la base de données:SQL Server 2008: Obtention d'interblocages ... sans verrous

UPDATE from Table A where rowID='123' 

Cependant, je suis d'obtenir une tonne d'erreurs de blocage (Exception SQL 1205) chaque fois que j'ai défini le niveau d'isolation supérieur à READ_UNCOMMITTED. Cela arrive même si je mets le verrouillage de ligne, le verrouillage de table et les conseils de verrouillage exclusifs! Et même dans Snapshot Isolation, qui n'utilise pas de verrous, je reçois toujours des erreurs de blocage.

J'ai exécuté une trace via SQL Profiler pour obtenir le graphe de blocage lorsque cela se produit, mais ce n'était pas très utile. Il a montré le processus de la victime, connecté à un «pool de threads», connecté à des centaines d'autres processus. Vous pouvez le vérifier ici:

http://i.stack.imgur.com/7rlv3.jpg

Quelqu'un at-il des conseils pour expliquer pourquoi cela se produit? Je suis devenu fou ces derniers jours en essayant de comprendre. Mon hypothèse actuelle est qu'il s'agit d'un problème lié aux threads de travail disponibles dans mon instance de base de données, à la quantité de mémoire disponible ou à quelque chose qui n'est pas lié aux verrous réels au niveau de la requête.

Merci!

+0

[Déjà vu?] (Http://blogs.msdn.com/b/bartd/archive/2008/09/24/today-s-annoyingly-unwieldy-term-intra-query-parallel-thread- deadlocks.aspx) Est-ce que votre instruction update a un plan parallèle? –

+0

Et dites-vous que ces blocages ** jamais ** se produisent lorsque 'READ_UNCOMMITTED' est en vigueur? Ce n'est pas du tout évident pour moi comment cela affecterait l'instruction 'update' montrée. –

+0

Wow! Je ne m'attendais pas à une réponse aussi accablante en si peu de temps! Les interblocages se produisent toujours sous READ_UNCOMMITTED mais seulement quand il y a beaucoup, beaucoup, beaucoup plus de threads concurrents (environ 1000). Je m'excuse pour le flou. – akwok

Répondre

1

Les blocages/verrous comme ceux-ci sont étranges et pointent vers quelque chose en dehors du serveur SQL. Pour ce qui vaut la peine, nous avons eu beaucoup de problèmes d'impasse qui s'est avéré être le goulot d'étranglement du disque!

Je vous suggère d'exécuter perfmon (et évidemment après cela, beaucoup d'autres outils) et voir comment ça se passe.

1

Vous ne pouvez pas faire quoi que ce soit dans SQL Server sans verrous - même la requête la plus fondamentale plâtré avec NOLOCK déclarations émettra un verrou de schéma et probablement quelques verrous de page au strict minimum. Pour résoudre un interblocage, vous devez obtenir une trace d'interblocage T1204 (voir Deadlock Troubleshooting, Part 1 pour plus de détails) qui énumérera les verrous et les objets exacts impliqués dans l'impasse - cela devrait être assez d'informations pour comprendre (avec le quantité de grattage de la tête) exactement ce qui s'est mal passé.

Modification du niveau d'isolement sans comprendre pleinement la raison d'une impasse me semble un peu dangereux pour ...

Comme un pressentiment cela me rappelle une question que j'avais quelques années - est la supercondamnation déclaration UPDATE contre une déclaration SELECT? (La trace T1024 vous le dira) Et avez-vous un index non-cluster sur rowID? Si c'est le cas, vous pouvez jeter un oeil à this MSDN article, spécifiquement Exemple 6: Index non clusterisés. Si ce n'est pas le cas, jetez un coup d'œil à cet article, car cela peut vous aider à expliquer d'autres scénarios de blocage. Vous pouvez également afficher les résultats de la trace T1024 si vous avez besoin d'aide pour l'analyser.

6

Vous avez rencontré une bête plus ésotérique: un blocage de ressources. Ce que vous avez là est un thread qui ne peut pas générer des tâches enfant (sys.dm_os_tasks) pour exécuter son travail car tous les travailleurs (sys.dm_os_workers) sont occupés. À leur tour, les travailleurs occupés exécutent des tâches qui sont bloquées, probablement sur des verrous ordinaires, par la victime.

Il y a deux leçons que je vois ici pour ramener à la maison:

1) L'instruction UPDATE que vous avez envoyé tente d'aller en parallèle. Si la mise à jour est exactement comme vous l'avez posté, cela signifie une seule et unique chose: pas d'index sur rowId.

2) Vous avez rebondi sur le plafond supérieur réglé par max worker threads. Rien d'étonnant si l'on considère que vous abusez des threads dans le client (hundreds of concurrent threads to execute thousands of task) et multipliez cela dans le serveur en raison d'un parallélisme indésirable. Une conception sensée utiliserait une exécution asynchrone (BeginExecuteNonQuery) sur une connexion véritablement asynchrone (AsynchronousProcessing=true) et utiliserait un pool de requêtes en attente de sorte qu'elle ne dépasse pas un certain seuil. Plus probablement, encore, est que vous passeriez dans un lot entier de valeurs de mise à jour par un table valued parameter, puis mettre à jour un ensemble ou des lignes entières, en lots, dans une seule déclaration. Je comprends que tous mes liens sont pour .Net, pas pour Java, je m'en fiche, vous pouvez vous-même découvrir les fonctionnalités Java équivalentes.

Alors, même s'il est intéressant que vous ayez découvert un tel étreinte ésotérique, cela ne se manifeste que parce que votre design, bien ... craint.

+1

J'aurais +1 pour une analyse brillante .. mais tempérée avec -1 pour, dirons-nous, la livraison? Omission de 2 mots ou plus aurait lu .. plus agréable? – RichardTheKiwi

+1

Merci pour la réponse, Remus. Je comprends que le design est nul, mais c'est fait exprès! Je travaille sur un projet qui est censé montrer que 1.) différentes anomalies de lecture existent en fonction du type de niveau d'isolation choisi et pour montrer 2.) des résultats de performance empiriques pour choisir un niveau d'isolation qui limite plus la concurrence que nécessaire. Dans tous les cas, vos commentaires ont du sens, et j'y reviendrai un peu plus; Au moins, vous étiez en mesure de me donner une direction pour faire d'autres lectures sur :-) – akwok

+0

@akwok: Je vois. Considérez que le UPDATE doit d'abord trouver les lignes à mettre à jour, puis les mettre à jour. La partie 'trouver' * est * influencée par le niveau d'isolement. Même REPEATABLE_READ peut choisir de scanner à un niveau de verrouillage de granularité élevé (page, tableau). La deuxième chose à considérer est que la partie 'mises à jour' sera en conflit avec une autre mise à jour sous tous les niveaux d'isolation, y compris l'instantané. En outre, les mises à jour de * lignes * différentes seront en conflit, en raison des collisions de hachage: http://rusanu.com/2009/05/29/lockres-collision-probability-magic-marker-16777215/ –