MySQL
SELECT ... FOR UPDATE avec UPDATE
En utilisant des transactions avec InnoDB (validation automatique désactivée), un SELECT ... FOR UPDATE
permet à une session de verrouiller temporairement un enregistrement particulier (ou des enregistrements) afin qu'aucune autre session ne puisse le mettre à jour. Ensuite, au sein de la même transaction, la session peut effectivement effectuer une UPDATE
sur le même enregistrement et valider ou annuler la transaction. Cela vous permettrait de verrouiller l'enregistrement afin qu'aucune autre session ne puisse le mettre à jour pendant que vous faites peut-être une autre logique métier.
Ceci est accompli avec le verrouillage. InnoDB utilise des index pour verrouiller les enregistrements, donc verrouiller un enregistrement existant semble facile - il suffit de verrouiller l'index de cet enregistrement.
SELECT ... FOR UPDATE avec INSERT
Cependant, pour utiliser SELECT ... FOR UPDATE
avec INSERT
, comment verrouiller un index pour un enregistrement qui n'existe pas encore ? Si vous utilisez le niveau d'isolement par défaut de REPEATABLE READ
, InnoDB utilisera également gap serrures. Tant que vous connaissez l'id
(ou même une plage d'identifiants) à verrouiller, alors InnoDB peut verrouiller l'espace afin qu'aucun autre enregistrement ne puisse être inséré dans cet espace jusqu'à ce que nous en ayons fini.
Si votre id
était une colonne auto-incrémentée, puis SELECT ... FOR UPDATE
avec INSERT INTO
serait problématique car vous ne sauriez pas ce que le nouveau id
était jusqu'à ce que vous l'insériez. Cependant, puisque vous connaissez le id
que vous souhaitez insérer, SELECT ... FOR UPDATE
avec INSERT
fonctionnera.
MISE EN GARDE
Au niveau d'isolement par défaut, SELECT ... FOR UPDATE
sur un enregistrement inexistant pas bloquer les autres transactions. Donc, si deux transactions font toutes les deux un SELECT ... FOR UPDATE
sur le même enregistrement d'index inexistant, ils obtiendront tous les deux le verrou et aucune des transactions ne pourra mettre à jour l'enregistrement. En fait, s'ils essaient, un blocage sera détecté.
Par conséquent, si vous ne voulez pas faire face à un blocage, vous pouvez simplement procéder comme suit :
INSÉRER DANS ...
Démarrez une transaction et effectuez le INSERT
. Faites votre logique métier et validez ou annulez la transaction. Dès que vous faites le INSERT
sur l'index d'enregistrement inexistant sur la première transaction, toutes les autres transactions seront bloquées si elles tentent de INSERT
un enregistrement avec le même index unique. Si la deuxième transaction tente d'insérer un enregistrement avec le même index après que la première transaction a validé l'insertion, elle obtiendra une erreur "clé en double". Manipulez en conséquence.
SÉLECTIONNER... VERROUILLER EN MODE PARTAGE
Si vous sélectionnez avec LOCK IN SHARE MODE
avant le INSERT
, si une transaction précédente a inséré cet enregistrement mais n'a pas encore été validée, le SELECT ... LOCK IN SHARE MODE
bloquera jusqu'à ce que la transaction précédente soit terminée.
Donc, pour réduire le risque d'erreurs de clé en double, en particulier si vous maintenez les verrous pendant un certain temps tout en exécutant la logique métier avant de les valider ou de les annuler :
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
- Si aucun enregistrement n'est renvoyé, alors
INSERT INTO FooBar (foo, bar) VALUES (?, ?)