web-dev-qa-db-fra.com

Quand utiliser SELECT ... FOR UPDATE?

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 UPDATE Devrait être utilisé?

Donné:

  • chambres [id]
  • balises [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 salles et leurs balises, mais doit différencier les salles sans balises des salles supprimées. Si SELECT ... FOR UPDATE n'est pas utilisé, voici ce qui pourrait arriver:

  • Initialement:
    • chambres contient [id = 1]
    • les balises contiennent [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;
  • Fil 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 pièce 1 n'a pas de balises, mais en réalité la pièce a été supprimée. Pour résoudre ce problème, le fil 1 devrait SELECT id FROM rooms FOR UPDATE, Empêchant ainsi le fil 2 de supprimer de rooms jusqu'à ce que le fil 1 soit terminé. Est-ce exact?

Question 2: Quand faut-il utiliser SERIALIZABLE isolement de transaction versus READ_COMMITTED Avec SELECT ... FOR UPDATE?

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

101
Gili

Le seul moyen portable d'assurer la cohérence entre les salles et les balises et de s'assurer que les salles ne sont jamais restituées après leur suppression est de les verrouiller avec SELECT FOR UPDATE.

Cependant, dans certains systèmes, le verrouillage est un effet secondaire du contrôle de la simultanéité et vous obtenez les mêmes résultats sans spécifier FOR UPDATE explicitement.


Pour résoudre ce problème, le fil 1 devrait SELECT id FROM rooms FOR UPDATE, empêchant ainsi le thread 2 de supprimer de rooms jusqu'à ce que le thread 1 soit terminé. Est-ce exact?

Cela dépend du contrôle de simultanéité utilisé par votre système de base de données.

  • MyISAM dans MySQL (et plusieurs autres anciens systèmes) verrouille la table entière pendant toute la durée d'une requête.

  • Dans SQL Server, SELECT les requêtes placent des verrous partagés sur les enregistrements/pages/tables qu'elles ont examinées, tandis que DML les requêtes placent des verrous de mise à jour (qui sont ensuite promus en exclusivité ou rétrogradés en verrous partagés). Les verrous exclusifs étant incompatibles avec les verrous partagés, la requête SELECT ou DELETE sera verrouillée jusqu'à ce qu'une autre session soit validée.

  • Dans les bases de données qui utilisent MVCC (comme Oracle, PostgreSQL, MySQL avec InnoDB,), une requête DML crée une copie du disque (d'une manière ou d'une autre) et généralement les lecteurs ne bloquent pas les rédacteurs et vice versa. Pour ces bases de données, un SELECT FOR UPDATE serait utile: il verrouillerait soit SELECT ou la requête DELETE jusqu'à ce qu'une autre session soit validée, exactement comme SQL Server Est-ce que.

Quand faut-il utiliser REPEATABLE_READ isolement de transaction par rapport à READ_COMMITTED avec SELECT ... FOR UPDATE?

Généralement, REPEATABLE READ n'interdit pas les lignes fantômes (les lignes apparaissant ou disparaissant dans une autre transaction plutôt que d'être modifiées)

  • Dans Oracle et versions antérieures PostgreSQL versions, REPEATABLE READ est en fait un synonyme de SERIALIZABLE. En gros, cela signifie que la transaction ne voit pas les modifications apportées après son démarrage. Donc, dans cette configuration, le dernier Thread 1 requête retournera la pièce comme si elle n’avait jamais été supprimée (ce que vous vouliez ou non). Si vous ne souhaitez pas afficher les salles après leur suppression, vous devez verrouiller les lignes avec SELECT FOR UPDATE

  • Dans InnoDB, REPEATABLE READ et SERIALIZABLE sont deux choses différentes: les lecteurs du mode SERIALIZABLE définissent les verrous next-key sur les enregistrements qu’ils évaluent, empêchant ainsi efficacement la concurrence simultanée DML. Donc, vous n'avez pas besoin d'un SELECT FOR UPDATE en mode sérialisable, mais en a besoin en REPEATABLE READ ou READ COMMITED.

Notez que la norme sur les modes d'isolation stipule que vous ne voyez pas certains défauts dans vos requêtes, mais ne définit pas comment (avec le verrouillage ou avec MVCC ou autrement).

Quand je dis "tu n’as pas besoin de SELECT FOR UPDATE "J'aurais vraiment dû ajouter" en raison des effets secondaires de la mise en œuvre de certains moteurs de base de données ".

70
Quassnoi

Réponses courtes:

Q1: oui.

Q2: Peu importe ce que vous utilisez.

Longue réponse:

UNE select ... for update va (comme cela 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 de l'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;

Depuis les lignes affectées par my_condition sont verrouillés, aucune autre transaction ne peut les modifier de quelque manière que ce soit et, par conséquent, le niveau d’isolation de la transaction ne fait aucune différence ici.

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

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

25
Colin 't Hart