Le verrouillage des tables empêche les autres utilisateurs de base de données d'affecter les lignes / tables que vous avez verrouillées. Mais les verrous, en eux-mêmes, ne garantiront PAS que votre logique sortira dans un état cohérent.
Pensez à un système bancaire. Lorsque vous payez une facture en ligne, il y a au moins deux comptes concernés par la transaction: Votre compte, sur lequel l'argent est prélevé. Et le compte du séquestre, dans lequel l'argent est transféré. Et le compte de la banque, sur lequel ils déposeront avec plaisir tous les frais de service facturés lors de la transaction. Étant donné (comme tout le monde le sait de nos jours) que les banques sont extraordinairement stupides, disons que leur système fonctionne comme ceci:
$balance = "GET BALANCE FROM your ACCOUNT";
if ($balance < $amount_being_paid) {
charge_huge_overdraft_fees();
}
$balance = $balance - $amount_being paid;
UPDATE your ACCOUNT SET BALANCE = $balance;
$balance = "GET BALANCE FROM receiver ACCOUNT"
charge_insane_transaction_fee();
$balance = $balance + $amount_being_paid
UPDATE receiver ACCOUNT SET BALANCE = $balance
Désormais, sans verrous ni transactions, ce système est vulnérable à diverses conditions de concurrence, dont la plus importante est plusieurs paiements effectués sur votre compte ou sur le compte du destinataire en parallèle. Bien que votre code ait récupéré votre solde et effectue les énormes_overdraft_fees () et ainsi de suite, il est tout à fait possible qu'un autre paiement exécute le même type de code en parallèle. Ils récupéreront votre solde (disons 100 $), effectueront leurs transactions (retirez les 20 $ que vous payez et les 30 $ qu'ils vous foutent), et maintenant les deux chemins de code ont deux soldes différents: 80 $ et 70 $. En fonction de celui qui se termine en dernier, vous vous retrouverez avec l'un de ces deux soldes dans votre compte, au lieu des 50 $ avec lesquels vous auriez dû vous retrouver (100 $ - 20 $ - 30 $). Dans ce cas, "erreur bancaire en votre faveur"
Maintenant, disons que vous utilisez des verrous. Le paiement de votre facture (20 $) arrive en premier, il gagne et verrouille l'enregistrement de votre compte. Maintenant, vous avez une utilisation exclusive et pouvez déduire les 20 $ du solde, et réécrire le nouveau solde en paix ... et votre compte se termine avec 80 $ comme prévu. Mais ... euh ... vous essayez d'aller mettre à jour le compte du destinataire, et il est verrouillé, et verrouillé plus longtemps que le code ne le permet, expirant votre transaction ... Nous avons affaire à des banques stupides, donc au lieu d'avoir une erreur appropriée manipulation, le code tire juste un exit()
, et vos 20 $ disparaissent dans une bouffée d'électrons. Maintenant, vous êtes à 20 $ et vous devez toujours 20 $ au destinataire, et votre téléphone est repris.
Alors ... saisissez des transactions. Vous démarrez une transaction, vous débitez votre compte de 20 $, vous essayez de créditer le destinataire de 20 $ ... et quelque chose explose à nouveau. Mais cette fois, au lieu de cela exit()
, le code peut tout simplement faire rollback
, et pouf, vos 20 $ sont ajoutés par magie à votre compte.
En fin de compte, cela se résume à ceci:
Les verrous empêchent quiconque d'interférer avec les enregistrements de la base de données que vous traitez. Les transactions empêchent les erreurs "ultérieures" d'interférer avec les choses "antérieures" que vous avez faites. Ni l'un ni l'autre ne peuvent garantir que les choses se passent bien à la fin. Mais ensemble, ils le font.
dans la leçon de demain: The Joy of Deadlocks.
Vous voulez une transaction
SELECT ... FOR UPDATE
ou à l'SELECT ... LOCK IN SHARE MODE
intérieur d'une transaction, comme vous l'avez dit, car normalement les SELECT, qu'ils soient dans une transaction ou non, ne verrouillent pas une table. Celui que vous choisirez dépendra du fait que vous souhaitez que d'autres transactions puissent lire cette ligne pendant que votre transaction est en cours.http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
START TRANSACTION WITH CONSISTENT SNAPSHOT
ne fera pas l'affaire pour vous, car d'autres transactions peuvent encore arriver et modifier cette ligne. Ceci est mentionné en haut du lien ci-dessous.http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html
la source
Les concepts de transaction et les verrous sont différents. Cependant, la transaction utilisait des verrous pour l'aider à suivre les principes ACID. Si vous voulez que la table empêche les autres de lire / écrire au même moment pendant que vous êtes en lecture / écriture, vous avez besoin d'un verrou pour ce faire. Si vous voulez vous assurer de l'intégrité et de la cohérence des données, il vaut mieux utiliser les transactions. Je pense que des concepts mixtes de niveaux d'isolement dans les transactions avec des verrous. Veuillez rechercher les niveaux d'isolement des transactions, SERIALIZE doit être le niveau souhaité.
la source
J'ai eu un problème similaire lors de la tentative de a
IF NOT EXISTS ...
, puis de l'exécution d'uneINSERT
condition de concurrence lorsque plusieurs threads mettaient à jour la même table.J'ai trouvé la solution au problème ici: Comment écrire des requêtes INSERT IF NOT EXISTS en SQL standard
Je me rends compte que cela ne répond pas directement à votre question, mais le même principe de vérification et d'insertion en une seule déclaration est très utile; vous devriez pouvoir le modifier pour effectuer votre mise à jour.
la source
Vous êtes confondu avec le verrouillage et la transaction. Ce sont deux choses différentes dans RMDB. Le verrouillage empêche les opérations simultanées pendant que la transaction se concentre sur l'isolation des données. Consultez cet excellent article pour la clarification et une solution élégante.
la source
J'utiliserais un
pour commencer, et un
pour finir avec.
Tout ce que vous faites entre les deux est isolé des autres utilisateurs de votre base de données si votre moteur de stockage prend en charge les transactions (qui est InnoDB).
la source