web-dev-qa-db-fra.com

Ma requête SQL utilisera-t-elle des données périmées? Comment puis-je l'empêcher?

J'ai deux tables (SJob & SJobDependent) que je dois joindre pour une certaine logique dans une procédure stockée. Ils ont tous deux une colonne (job) qui les relie dans une relation un-à-plusieurs - un SJob enregistrement pour zéro ou plusieurs enregistrements SJobDependent.

Voici ma requête SQL:

-- Return any records that are active and have no unsatisfied dependencies.
SELECT * FROM SJob
LEFT JOIN SJobDependent
    ON SJob.job = SJobDependent.job
    AND SJobDependent.satisfied = 0
WHERE SJobDependent.jobDependentID IS NULL
AND SJob.state = 'active'

Voici le plan d'exécution réel de SQL Server Studio:

SQL Server Studio Actual Execution Plan

En raison de la façon dont le code est écrit:

// Pseudo-code:

// SJob record is added with SJob.state = 'ready'.

// Related SJobDependent record(s) are added.

// SJob record is updated to SJob.state = 'active'.

Je crains que cela ne se produise lorsque la requête SQL s'exécute:

  1. Scannez SJobDependent.
  2. Enregistrement (s) SJobDependent inséré.
  3. Lancer l'analyse de SJob. SJob.state est "prêt".
  4. SJob est mis à jour. Cela bloque la lecture de SJob?
  5. Fin de l'analyse de SJob. SJob.state est "actif".

Le problème que je crains est que ma requête SQL renvoie SJob enregistrements trouvés dans l'état "actif" (SJob.state = 'active'), mais ne parvient pas à voir les enregistrements SJobDependent associés.

Ce problème est-il susceptible de se produire ou suis-je en train de sur-analyser la requête SQL?

S'il s'agit d'un problème légitime à craindre, que puis-je faire pour le résoudre? Je suis ouvert aux solutions.

Une idée que j'ai eue est de forcer l'analyse de SJobDependent à se produire après l'analyse de SJob. Est-ce seulement possible? Quelles sont les implications/conséquences de faire cela?

Les analyses affichées dans le plan d'exécution réel se produisent-elles dans un ordre particulier ou est-elle toujours aléatoire d'un appel à l'autre?

REMARQUE: Comme indiqué dans la réponse d'AMtwo, le niveau d'isolement en lecture répétable ne résoudra probablement pas mon problème, car qu'il ne prend effet que lorsque la lecture commence.

6
Max Jacob

Si vous utilisez le niveau d'isolement par défaut dans SQL Server (lecture validée), vous pouvez certainement rencontrer toutes sortes de problèmes liés aux lectures incohérentes. Paul White décrit les problèmes ici .

Si vous souhaitez que vos requêtes de lecture lisent des données qui sont entièrement cohérentes avec leur apparence à un moment donné, je vous recommande de considérer Lire l'isolement de l'instantané validé (RCSI). Avec RCSI, votre requête renverra des données cohérentes à un seul point dans le temps (le début de votre requête). Si l'utilisateur A démarre une requête SELECT pendant que l'utilisateur B effectue simultanément des mises à jour, l'utilisateur A lira l '"ancienne" valeur car il lira un instantané des données, qui est cohérente avec le début de la requête.

Le problème avec RCSI est qu'il s'agit d'un paramètre au niveau de la base de données. Contrairement à Read Uncommitted, vous ne pouvez pas le définir comme paramètre de portée de session. Vous devrez considérer ce changement plus globalement avant d'effectuer le changement. Cependant, d'une manière générale, si vous avez besoin de lectures cohérentes pour cette requête, vous probablement voulez des lectures cohérentes pour l'ensemble de l'application.

Bien que le niveau d'isolement lecture répétable puisse sembler attrayant pour résoudre votre problème, notez ce détail dans le message lié:

Le niveau d'isolation de lecture reproductible fournit une garantie que les données ne changeront pas pendant la durée de la transaction une fois qu'elles auront été lire pour la première fois.

Cela signifie que les données peuvent toujours être modifiées avant d'être consultées, mais pendant le temps où votre requête est en cours d'exécution. Il est également soumis à certaines des mêmes lectures incohérentes que le niveau d'isolement Read Committed - notamment les fantômes.

8
AMtwo

Vous devez réfléchir au fonctionnement de la charge de travail dans son ensemble, à la façon dont votre ensemble d'instructions voit le travail effectué par les autres et à la façon dont vous voyez le leur. Vous devez également prendre en compte les délais possibles des actions à partir de deux flux de travail exécutés simultanément.

Ce n'est pas clair à partir de votre question, mais je vais supposer que la requête ("Voici ma requête SQL") s'exécute dans une session (S1) et le pseudo-code s'exécute dans une autre (S2). Je suppose que chaque passage dans le pseudo-code n'insère qu'un seul travail. La question est de savoir quel travail de S2 peut voir S1 puisque S1 peut s'exécuter, en tout ou en partie, à tout moment dans le flux de S2?

Je vais dire "aucun". Je dis cela parce que la requête recherche spécifiquement SJob.state = 'active'. S2 insère une valeur dans SJob avec l'état "prêt". Donc, S1 ne lira pas cette ligne. Oui, la ligne peut être inspectée par S1 (selon le niveau d'isolement de S1) mais elle sera rejetée en raison de la valeur d'état et ne fera jamais partie du jeu de résultats de S1. Ainsi, même si SJobDependent est le côté de génération de la jointure de hachage, toutes les lignes récupérées seront rejetées dans l'opérateur Filter et ne feront jamais partie de la sortie de S1.

Ce n'est que lorsque des lignes existent dans les deux tables que S2 définit l'état sur une valeur que S1 peut lire. Cette mise à jour est garantie d'être atomique (elle fonctionne entièrement ou est entièrement annulée). Étant donné qu'une seule valeur dans une ligne est mise à jour, elle sera isolée - S1 ne peut jamais voir une valeur à mi-chemin entre "prêt" et "actif".


Sans le prédicat sur le statut, il aurait pu y avoir des problèmes. Tant que S1 s'exécute en LECTURE COMMISE ou supérieure, cependant, il n'y aura toujours pas de problème. S2 pourrait mettre une transaction explicite sur tout le bloc. Ensuite, ses verrous exclusifs seraient maintenus jusqu'à la fin (jusqu'à ce qu'un ensemble cohérent de lignes existe dans les deux tables). S1 aurait terminé avant que ces verrous soient en place, ne voyant que des données cohérentes, ou attendrait que ces verrous soient à nouveau libérés pour voir des données cohérentes. Si S1 s'exécute avec READ UNCOMMITTED (ou NOLOCK, même chose), il verrait le travail de S2 à mi-chemin et des erreurs s'ensuivraient.

1
Michael Green