10
votes

Deadlock causés par Select Rejoindre Déclaration avec SQL Server

Lors de l'exécution d'une instruction SELECT avec une jointure de deux tables SQL Server semble Verrouillez les deux tables de la déclaration individuellement. Par exemple par une requête comme Ceci:

T1: SELECT ... FROM ... JOIN ...
T1: UPDATE table1 SET ... WHERE id = ?
T2: SELECT ... FROM ... JOIN ... (locks table2, then blocked by lock on table1)
T1: UPDATE table2 SET ... WHERE id = ?


1 commentaires

Si c'est une question, la réponse est non. Cela se produit avec tous les niveaux d'isolation, sauf peut-être lire non engagé que je n'ai pas testé, car je ne veux pas que les transactions puissent lire une demi-mise à jour des données.


3 Réponses :


5
votes

Cela n'arrivera jamais sous l'isolement d'instantané, lorsque les lecteurs ne bloquent pas les écrivains. Autre que cela, il n'y a aucun moyen d'empêcher de telles choses. J'ai écrit beaucoup de scripts de repro viré ici: reproduisant des blocages impliquant une seule table

EDIT:

Je n'ai pas accès à SQL 2000, mais j'essaierais de sérialiser l'accès à l'objet en utilisant sp_getaplock, de sorte que la lecture et les modifications ne fonctionnent jamais simultanément. Si vous ne pouvez pas utiliser sp_getaplock, déployez votre propre mutex.


1 commentaires

La réponse m'a aidé à trouver une solition avec SQL Server 2005 ou plus récente. Mais le logiciel est toujours utilisé avec SQL Server 2000 également, je peux probablement mettre en œuvre différentes solutions pour les deux versions. Une solution qui fonctionne pour toutes les versions ou une solution différente pour SQL Server 2000 serait appréciée.



-1
votes

Je faisais face au même problème. L'utilisation de la commande de force de requête résoudra ce problème. L'inconvénient est que vous ne serez pas en mesure de tirer parti de la meilleure planification que la requête Optimizer a pour votre requête, mais cela empêchera l'impasse.

Donc (ceci est de "Bill The Lizard") si vous avez une requête de table1 Rejoindre Gauche Table2 et votre clause où la clause WHERE contient uniquement des colonnes de TABLET2, le plan d'exécution sélectionnera normalement les lignes de la table2, puis recherchez les lignes de la table1. Avec un petit résultat de la table2, seules quelques rangées de TABLE1 doivent être récupérées. Avec la commande de force Tout d'abord, toutes les lignes de TABLE1 doivent être extraites, car elle n'a pas de clause où, les lignes de la table2 sont jointes et que le résultat est filtré à l'aide de la clause WHERE. Ainsi dégradant les performances.

Mais si vous savez que cela ne sera pas le cas, utilisez ceci. Vous voudrez peut-être optimiser la requête manuellement.

La syntaxe est xxx


3 commentaires

En fait, j'ai écrit le commentaire sur la performance, semble que la pile dépassement a gâché votre réponse et mon commentaire, je me suis déjà demandé pourquoi elle a disparu. Le problème n'est pas seulement la performance avec la commande de force, c'est aussi le verrouillage. Si vous utilisez la commande de la force et que le plan d'exécution lisait toutes les lignes de Table1, il met également un verrou partagé sur eux. Donc, fondamentalement, chaque requête qui n'utilise que des colonnes à partir de TABLE2, verrouille toute la table1 en faisant des mises à jour impossibles. Ce n'est pas une seule requête qui doit fonctionner, le SELECT doit fonctionner avec n'importe quelle clause.


Donc, vous ne voulez pas verrouiller la table1, tout en pouvant vous joindre à table2? Dans mon cas, j'avais des insertions dans le tableau1, puis dans le tableau2. Et Optimizer de requête cueillait l'ordre inverse en cas de jointure. Si impasse. Ajout de la commande de force résolue que, d'aucune impasse. NOTE ICI ICI QUE JE VOULEZ TOUJOURS VOULEZ TOUJOURS VERROUILLER SUR LA TABLEAGE1. Les insertions peuvent attendre jusqu'à la finition de sélection. Mais il n'y aura pas d'impasse.


Aucun verrouillage n'est ok sur la table1, mais avec la commande de force, il verrouille toutes les lignes de la table au lieu de seulement les lignes qui doivent être jointes avec les rangées de la table2. Allocation de verrous pour toutes les lignes peut également devenir un problème, car il s'agit d'un processus de ressources qui doivent allouer. Si le serveur SQL exécute notre mémoire pour les verrous, les processus peuvent être des blocages sur l'allocation de la mémoire.



0
votes

Une autre façon de résoudre ce problème est de diviser la sélection ... de ... Inscrivez-vous dans plusieurs instructions de sélection. Définissez le niveau d'isolation pour lire commis. Utilisez une variable de table pour les données de tuyau de sélectionner à être jointes à d'autres. Utilisez des inserts distincts pour filtrer dans ces variables de table.

Donc, si j'ai deux tables A, B. J'insère / la mise à jour dans A, puis B. Où que l'optimiseur de requête de SQL préfère lire B d'abord et A. Je diviserai le simple sélectionner en 2 sélectionneurs . Je vais d'abord lire B. puis transmettez ces données à la prochaine instruction SELECT qui lit un.

Ici, l'impasse ne se produira pas car les serrures de lecture sur la table B seront publiées dès que la première instruction est effectuée.

PS J'ai fait face à cette question et cela a fonctionné très bien. Beaucoup mieux que la réponse de mon ordre de force.


0 commentaires