Durch das Sperren von Tabellen wird verhindert, dass andere DB-Benutzer die von Ihnen gesperrten Zeilen / Tabellen beeinflussen. Sperren an und für sich stellen jedoch NICHT sicher, dass Ihre Logik in einem konsistenten Zustand ausgegeben wird.
Denken Sie an ein Bankensystem. Wenn Sie eine Rechnung online bezahlen, sind mindestens zwei Konten von der Transaktion betroffen: Ihr Konto, von dem das Geld abgezogen wird. Und das Konto des Empfängers, auf das das Geld überwiesen wird. Und das Konto der Bank, auf das sie gerne alle für die Transaktion berechneten Servicegebühren einzahlen. Angesichts der Tatsache, dass Banken (wie heutzutage jeder weiß) außerordentlich dumm sind, nehmen wir an, dass ihr System folgendermaßen funktioniert:
$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
Jetzt, ohne Sperren und ohne Transaktionen, ist dieses System anfällig für verschiedene Rennbedingungen, von denen die größte darin besteht, dass mehrere Zahlungen auf Ihrem Konto oder auf dem Konto des Empfängers parallel ausgeführt werden. Während Ihr Code Ihr Guthaben abgerufen hat und die riesigen_overdraft_fees () und so weiter ausführt, ist es durchaus möglich, dass bei einer anderen Zahlung dieselbe Art von Code parallel ausgeführt wird. Sie werden Ihr Guthaben abrufen (z. B. 100 US-Dollar), ihre Transaktionen durchführen (die 20 US-Dollar, die Sie bezahlen, und die 30 US-Dollar, mit denen sie Sie verarschen, herausnehmen), und jetzt haben beide Codepfade zwei unterschiedliche Guthaben: 80 US-Dollar und $ 70. Abhängig davon, welches zuletzt endet, erhalten Sie eines dieser beiden Guthaben auf Ihrem Konto, anstatt der 50 US-Dollar, die Sie hätten erhalten sollen (100 bis 20 bis 30 US-Dollar). In diesem Fall "Bankfehler zu Ihren Gunsten"
Angenommen, Sie verwenden Sperren. Ihre Rechnungszahlung (20 US-Dollar) geht zuerst auf die Pipe, sodass Sie Ihren Kontodatensatz gewinnen und sperren. Jetzt haben Sie die ausschließliche Verwendung und können die 20 USD vom Guthaben abziehen und das neue Guthaben in Ruhe zurückschreiben ... und Ihr Konto erhält erwartungsgemäß 80 USD. Aber ... äh ... Sie versuchen, das Konto des Empfängers zu aktualisieren, und es ist gesperrt und länger gesperrt, als es der Code zulässt, wodurch Ihre Transaktion zeitlich begrenzt wird ... Wir haben es mit dummen Banken zu tun, anstatt einen richtigen Fehler zu haben Handling, der Code zieht nur ein exit()
, und Ihre 20 Dollar verschwinden in einem Elektronenstoß. Jetzt haben Sie 20 US-Dollar verloren, und Sie schulden dem Empfänger immer noch 20 US-Dollar, und Ihr Telefon wird wieder in Besitz genommen.
Also ... Transaktionen eingeben. Sie starten eine Transaktion, Sie belasten Ihr Konto mit 20 USD, Sie versuchen, dem Empfänger 20 USD gutzuschreiben ... und etwas explodiert erneut. Aber diesmal kann exit()
der Code nicht mehr rollback
, und Ihre 20 US-Dollar werden Ihrem Konto auf magische Weise wieder gutgeschrieben.
Am Ende läuft es darauf hinaus:
Sperren verhindern, dass andere Benutzer Datenbankeinträge stören, mit denen Sie sich befassen. Transaktionen verhindern, dass "spätere" Fehler "frühere" Dinge stören, die Sie getan haben. Keiner von beiden allein kann garantieren, dass die Dinge am Ende gut laufen. Aber zusammen tun sie es.
in der morgigen Lektion: Die Freude an Deadlocks.
Sie möchten eine
SELECT ... FOR UPDATE
oderSELECT ... LOCK IN SHARE MODE
innerhalb einer Transaktion, wie Sie sagten, da normalerweise SELECTs, unabhängig davon, ob sie sich in einer Transaktion befinden oder nicht, eine Tabelle nicht sperren. Welche Sie auswählen, hängt davon ab, ob andere Transaktionen diese Zeile lesen können, während Ihre Transaktion ausgeführt wird.http://dev.mysql.com/doc/refman/5.0/en/innodb-locking-reads.html
START TRANSACTION WITH CONSISTENT SNAPSHOT
wird den Trick nicht für Sie tun, da andere Transaktionen noch kommen und diese Zeile ändern können. Dies wird ganz oben auf dem Link unten erwähnt.http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html
quelle
Transaktionskonzepte und Sperren sind unterschiedlich. Die Transaktion verwendete jedoch Sperren, um die ACID-Prinzipien zu befolgen. Wenn Sie möchten, dass die Tabelle verhindert, dass andere zum gleichen Zeitpunkt lesen / schreiben, während Sie lesen / schreiben, benötigen Sie dazu eine Sperre. Wenn Sie die Datenintegrität und -konsistenz sicherstellen möchten, sollten Sie Transaktionen besser verwenden. Ich denke, gemischte Konzepte von Isolationsstufen bei Transaktionen mit Sperren. Bitte suchen Sie nach Isolationsstufen von Transaktionen. SERIALIZE sollte die gewünschte Stufe sein.
quelle
Ich hatte ein ähnliches Problem beim Versuch eines
IF NOT EXISTS ...
und dann beim Ausführen eines,INSERT
das eine Race-Bedingung verursachte, wenn mehrere Threads dieselbe Tabelle aktualisierten.Ich habe hier die Lösung für das Problem gefunden: So schreiben Sie INSERT IF NOT EXISTS-Abfragen in Standard-SQL
Mir ist klar, dass dies Ihre Frage nicht direkt beantwortet, aber das gleiche Prinzip, eine Prüfung und Einfügung als einzelne Aussage durchzuführen, ist sehr nützlich. Sie sollten es ändern können, um Ihr Update durchzuführen.
quelle
Sie sind mit Lock & Transaction verwechselt. Das sind zwei verschiedene Dinge in RMDB. Die Sperre verhindert gleichzeitige Vorgänge, während sich die Transaktion auf die Datenisolierung konzentriert. In diesem großartigen Artikel finden Sie Erläuterungen und eine elegante Lösung.
quelle
Ich würde eine verwenden
zu Beginn und a
zum Schluss mit.
Alles, was Sie dazwischen tun, ist von den anderen Benutzern Ihrer Datenbank isoliert, wenn Ihre Speicher-Engine Transaktionen unterstützt (InnoDB).
quelle