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 UPDATE
doit ê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]
- pièces contient
- 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 rooms
jusqu'à ce que Thread 1 soit terminé. Est-ce exact?
Question 2 : Quand faut-il utiliser l' SERIALIZABLE
isolation des transactions par rapport à READ_COMMITTED
avec 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.
REPEATABLE_READ
etREAD_COMMITTED
même les options portables? Les seuls résultats que j'obtiens pour ceux-ci sont pour le serveur MSSQLREAD COMMITTED
mode 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.select ... for update
onrooms
permettra toujoursroom_tags
d'être supprimé car ce sont des tables séparées. Vouliez-vous demander si lafor update
clause empêchera les suppressionsrooms
?Réponses:
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 UPDATE
explicitement.Cela dépend du contrôle d'accès concurrentiel utilisé par votre système de base de données.
MyISAM
inMySQL
(et plusieurs autres anciens systèmes) verrouille toute la table pour la durée d'une requête.Dans
SQL Server
, lesSELECT
requêtes placent des verrous partagés sur les enregistrements / pages / tables qu'ils ont examinés, tandis que lesDML
requê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êteSELECT
ouDELETE
se verrouille jusqu'à ce qu'une autre session soit validée.Dans les bases de données dont l' utilisation
MVCC
(commeOracle
,PostgreSQL
,MySQL
avecInnoDB
), uneDML
requê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, unSELECT FOR UPDATE
serait utile: cela verrouillerait l'uneSELECT
ou l'DELETE
autre de la requête jusqu'à ce qu'une autre session soit validée, tout comme leSQL Server
fait.En règle générale,
REPEATABLE READ
n'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
Oracle
lesPostgreSQL
versions antérieures et antérieures ,REPEATABLE READ
est en fait un synonyme deSERIALIZABLE
. Fondamentalement, cela signifie que la transaction ne voit pas les modifications apportées après son démarrage. Ainsi, dans cette configuration, la dernièreThread 1
requê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 READ
etSERIALIZABLE
sont des choses différentes: les lecteurs enSERIALIZABLE
mode définissent des verrous de clé suivante sur les enregistrements qu'ils évaluent, empêchant efficacement le concurrentDML
sur eux. Vous n'avez donc pas besoin d'unSELECT FOR UPDATE
mode sérialisable, mais vous en avez besoin dansREPEATABLE READ
ouREAD 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
MVCC
ou 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".la source
SERIALIZABLE
devrait être utilisé par rapport àREAD_COMMITTED
avecSELECT ... FOR UPDATE
. Pouvez-vous mettre à jour votre réponse pour refléter cette question mise à jour?SELECT FOR UPDATE
mode sérialisable", avecInnoDB
. Avec les autresMVCC
systèmes, les deux sont des synonymes et vous en avez besoinSELECT FOR UPDATE
.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 entreSELECT
la fin de la transaction. Alors, la réponse ne devrait-elle pas être simplementYes
avec les références ci-dessous?Réponses courtes:
Q1: Oui.
Q2: peu importe ce que vous utilisez.
Longue réponse:
Un
select ... for update
testera (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:
Étant donné que les lignes affectées par
my_condition
sont 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.
la source
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.