Quand utiliser SELECT… FOR UPDATE?

119

S'il vous plaît, aidez-moi à comprendre le cas d'utilisation derrière SELECT ... FOR UPDATE.

Question 1 : Est-ce que ce qui suit est un bon exemple de quand SELECT ... FOR UPDATEdoit être utilisé?

Donné:

  • salles [id]
  • tags [id, nom]
  • room_tags [room_id, tag_id]
    • room_id et tag_id sont des clés étrangères

L'application souhaite répertorier toutes les pièces et leurs balises, mais doit différencier les pièces sans balises des pièces qui ont été supprimées. Si SELECT ... FOR UPDATE n'est pas utilisé, ce qui pourrait arriver est:

  • Initialement:
    • pièces contient [id = 1]
    • balises contient [id = 1, name = 'cats']
    • room_tags contient [room_id = 1, tag_id = 1]
  • Fil 1: SELECT id FROM rooms;
    • returns [id = 1]
  • Fil 2: DELETE FROM room_tags WHERE room_id = 1;
  • Fil 2: DELETE FROM rooms WHERE id = 1;
  • Thread 2: [valide la transaction]
  • Fil 1: SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
    • renvoie une liste vide

Maintenant, le fil 1 pense que la salle 1 n'a pas de balises, mais en réalité la salle a été supprimée. Pour résoudre ce problème, Thread 1 devrait SELECT id FROM rooms FOR UPDATE, empêchant ainsi la suppression de Thread 2 roomsjusqu'à ce que Thread 1 soit terminé. Est-ce exact?

Question 2 : Quand faut-il utiliser l' SERIALIZABLEisolation des transactions par rapport à READ_COMMITTEDavec SELECT ... FOR UPDATE?

Les réponses devraient être portables (et non spécifiques à la base de données). Si ce n'est pas possible, veuillez expliquer pourquoi.

Gili
la source
2
Quel SGBDR utilisez-vous?
Quassnoi
2
@Quassnoi, comme mentionné au bas de la question, je recherche une solution portable (pas spécifique à la base de données).
Gili
2
Les options REPEATABLE_READet READ_COMMITTEDmême les options portables? Les seuls résultats que j'obtiens pour ceux-ci sont pour le serveur MSSQL
Billy ONeal
3
@BillyONeal: notez que les modes d'isolement garantissent que vous ne voyez pas les bizarreries qu'ils ne permettent pas, mais ne dites rien sur les bizarreries qu'ils autorisent. Cela signifie que la définition, par exemple, du READ COMMITTEDmode ne définit pas si vous verrez ou non les enregistrements validés par une autre transaction: cela garantit seulement que vous ne verrez jamais les enregistrements non validés.
Quassnoi
3
Un select ... for updateon roomspermettra toujours room_tagsd'être supprimé car ce sont des tables séparées. Vouliez-vous demander si la for updateclause empêchera les suppressions rooms?
Chris Saxon

Réponses:

84

Le seul moyen portable d’assurer la cohérence entre les pièces et les balises et de s’assurer que les pièces ne sont jamais renvoyées après leur suppression est de les verrouiller SELECT FOR UPDATE.

Cependant, dans certains systèmes, le verrouillage est un effet secondaire du contrôle d'accès concurrentiel et vous obtenez les mêmes résultats sans le spécifier FOR UPDATEexplicitement.


Pour résoudre ce problème, Thread 1 devrait SELECT id FROM rooms FOR UPDATE, empêchant ainsi la suppression de Thread 2 roomsjusqu'à ce que Thread 1 soit terminé. Est-ce exact?

Cela dépend du contrôle d'accès concurrentiel utilisé par votre système de base de données.

  • MyISAMin MySQL(et plusieurs autres anciens systèmes) verrouille toute la table pour la durée d'une requête.

  • Dans SQL Server, les SELECTrequêtes placent des verrous partagés sur les enregistrements / pages / tables qu'ils ont examinés, tandis que les DMLrequêtes placent des verrous de mise à jour (qui sont ensuite promus en verrous exclusifs ou rétrogradés en verrous partagés). Les verrous exclusifs sont incompatibles avec les verrous partagés, donc la requête SELECTou DELETEse verrouille jusqu'à ce qu'une autre session soit validée.

  • Dans les bases de données dont l' utilisation MVCC(comme Oracle, PostgreSQL, MySQLavec InnoDB), une DMLrequête crée une copie de l'enregistrement (en une ou une autre façon) et en général les lecteurs ne bloquent pas les écrivains et vice - versa. Pour ces bases de données, un SELECT FOR UPDATEserait utile: cela verrouillerait l'une SELECTou l' DELETEautre de la requête jusqu'à ce qu'une autre session soit validée, tout comme le SQL Serverfait.

Quand faut-il utiliser l' REPEATABLE_READisolation des transactions par rapport à READ_COMMITTEDavec SELECT ... FOR UPDATE?

En règle générale, REPEATABLE READn'interdit pas les lignes fantômes (lignes qui sont apparues ou ont disparu dans une autre transaction, plutôt que d'être modifiées)

  • Dans Oracleles PostgreSQLversions antérieures et antérieures , REPEATABLE READest en fait un synonyme de SERIALIZABLE. Fondamentalement, cela signifie que la transaction ne voit pas les modifications apportées après son démarrage. Ainsi, dans cette configuration, la dernière Thread 1requête renverra la salle comme si elle n'avait jamais été supprimée (ce qui peut ou non être ce que vous vouliez). Si vous ne souhaitez pas afficher les pièces après leur suppression, vous devez verrouiller les lignes avecSELECT FOR UPDATE

  • Dans InnoDB, REPEATABLE READet SERIALIZABLEsont des choses différentes: les lecteurs en SERIALIZABLEmode définissent des verrous de clé suivante sur les enregistrements qu'ils évaluent, empêchant efficacement le concurrent DMLsur eux. Vous n'avez donc pas besoin d'un SELECT FOR UPDATEmode sérialisable, mais vous en avez besoin dans REPEATABLE READou READ COMMITED.

Notez que la norme sur les modes d'isolation prescrit que vous ne voyez pas certaines bizarreries dans vos requêtes, mais ne définit pas comment (avec verrouillage ou avec MVCCou autre).

Quand je dis "vous n'avez pas besoin SELECT FOR UPDATE", j'aurais vraiment dû ajouter "à cause des effets secondaires de certaines implémentations de moteur de base de données".

Quassnoi
la source
1
Le dernier point est le nœud du problème, je pense: "vous n'avez pas besoin d'un SELECT FOR UPDATE en mode sérialisable, mais vous en avez besoin en REPEATABLE READ ou READ COMMITED".
Colin 't Hart
Vous avez raison. La deuxième question aurait dû demander quand SERIALIZABLEdevrait être utilisé par rapport à READ_COMMITTEDavec SELECT ... FOR UPDATE. Pouvez-vous mettre à jour votre réponse pour refléter cette question mise à jour?
Gili
1
@Gili: "vous n'avez pas besoin d'un SELECT FOR UPDATEmode sérialisable", avec InnoDB. Avec les autres MVCCsystèmes, les deux sont des synonymes et vous en avez besoin SELECT FOR UPDATE.
Quassnoi
1
Je trouve que le message de Colin répond mieux à mes questions spécifiques que votre réponse, mais j'apprécie toutes les références que vous avez fournies. J'accepterai une réponse qui combine le mieux les deux (réponses spécifiques en haut, références à l'appui ci-dessous).
Gili
This depends on the concurrency control your database system is using: Je pense que vous vous fendez les cheveux. Tous les cas que vous énumérez ci-dessous indiquent que la salle n'est pas supprimée entre SELECTla fin de la transaction. Alors, la réponse ne devrait-elle pas être simplement Yesavec les références ci-dessous?
Gili
33

Réponses courtes:

Q1: Oui.

Q2: peu importe ce que vous utilisez.

Longue réponse:

Un select ... for updatetestera (comme cela l'implique) sélectionner certaines lignes mais aussi les verrouiller comme si elles avaient déjà été mises à jour par la transaction en cours (ou comme si la mise à jour d'identité avait été effectuée). Cela vous permet de les mettre à jour à nouveau dans la transaction en cours, puis de les valider, sans qu'une autre transaction ne puisse modifier ces lignes de quelque manière que ce soit.

Une autre façon de voir les choses, c'est comme si les deux instructions suivantes étaient exécutées de manière atomique:

select * from my_table where my_condition;

update my_table set my_column = my_column where my_condition;

Étant donné que les lignes affectées par my_conditionsont verrouillées, aucune autre transaction ne peut les modifier de quelque manière que ce soit, et par conséquent, le niveau d'isolation des transactions ne fait aucune différence ici.

Notez également que le niveau d'isolation des transactions est indépendant du verrouillage: la définition d'un niveau d'isolation différent ne vous permet pas de contourner le verrouillage et de mettre à jour des lignes dans une transaction différente qui sont verrouillées par votre transaction.

Ce que les niveaux d'isolation des transactions garantissent (à différents niveaux), c'est la cohérence des données pendant que les transactions sont en cours.

Colin 't Hart
la source
1
Je pense What transaction isolation levels do guarantee [...] is the consistency of data once transactions are completed.que cela implique à tort que les niveaux d'isolement n'affectent pas ce qui se passe pendant une transaction. Je recommande de réviser cette section et de fournir plus de détails sur leur impact sur ce que vous voyez (ou ne voyez pas) lors d'une transaction.
Gili
1
Je trouve que votre message répond mieux à mes questions spécifiques que celui de Quassnoi mais j'apprécie toutes les références qu'il a fournies. J'accepterai une réponse qui combine le mieux les deux (réponses spécifiques en haut, références à l'appui ci-dessous).
Gili
Le verrouillage et l'isolement sont interchangeables compliqués. Alors, y a-t-il des livres pour en savoir plus?
Chao