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é:
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:
[id = 1]
[id = 1, name = 'cats']
[room_id = 1, tag_id = 1]
SELECT id FROM rooms;
returns [id = 1]
DELETE FROM room_tags WHERE room_id = 1;
DELETE FROM rooms WHERE id = 1;
SELECT tags.name FROM room_tags, tags WHERE room_tags.tag_id = 1 AND tags.id = room_tags.tag_id;
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.
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 derooms
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
avecSELECT ... 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 ".
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.