MySQL
SELECT ... FOR UPDATE avec UPDATE
, une SELECT ... FOR UPDATE
permet une session de verrouiller temporairement un enregistrement particulier les transactions, avec InnoDB (validation automatique désactivé) (ou dossiers) de sorte qu'aucune autre session peut mettre à jour il.Ensuite, au cours de la même transaction, la session peut effectivement exécuter un 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 alors que vous avez peut-être une autre logique métier.
Ceci est accompli avec le verrouillage. InnoDB utilise des index pour verrouiller les enregistrements, donc le verrouillage d'un enregistrement existant semble facile - il suffit de verrouiller l'index pour cet enregistrement.
SELECT ... FOR UPDATE avec INSERT
Toutefois, 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 les verrous gap. Tant que vous connaissez le id
(ou même la gamme d'identifiants) à verrouiller, alors InnoDB peut verrouiller l'écart afin qu'aucun autre enregistrement ne puisse être inséré dans cet espace jusqu'à ce que nous en ayons fini avec lui.
Si votre colonne était un incrément-auto colonne id
, puis SELECT ... FOR UPDATE
avec INSERT INTO
serait problématique parce que vous ne savez pas ce que le nouveau id
était jusqu'à ce que vous l'avez inséré. Cependant, puisque vous connaissez le id
que vous souhaitez insérer, SELECT ... FOR UPDATE
avec INSERT
fonctionnera.
CAVEAT
Sur le niveau d'isolation par défaut, SELECT ... FOR UPDATE
sur un enregistrement inexistant ne fait pas bloc d'autres transactions. Donc, si deux transactions font SELECT ... FOR UPDATE
sur le même enregistrement d'index inexistant, elles verront toutes deux le verrou, et aucune des deux transactions ne pourra mettre à jour l'enregistrement. En fait, si les deux essayent, une impasse sera détectée, et une sera annulée.
Par conséquent, si vous ne voulez pas faire face à une impasse, vous pourriez juste faire ce qui suit:
INSERT INTO ...
Démarrer une transaction et effectuer la 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 bloqueront si elles tentent INSERT
un enregistrement avec le même index unique. Si la seconde transaction tente d'insérer un enregistrement avec le même index après que la première transaction ait validé l'insertion, une erreur "clé dupliquée" sera générée. Manipuler en conséquence.
SELECT ... LOCK IN SHARE MODE
Si vous sélectionnez avec LOCK IN SHARE MODE
avant la INSERT
, si une transaction antérieure a inséré ce dossier, mais n'a pas encore engagé, le SELECT ... LOCK IN SHARE MODE
va bloquer jusqu'à ce que la transaction précédente a completé.
Donc, pour réduire les risques de double erreurs clés, surtout si vous tenez les verrous pendant un certain temps tout en effectuant la logique métier avant de les commettre ou les faire reculer:
SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
- Si aucun enregistrement retourné, puis
INSERT INTO FooBar (foo, bar) VALUES (?, ?)
Est-ce que MSSQL supporte même cette syntaxe? –
Il a "avec updlock" qui je crois est à peu près la même chose. –