1. Ursprung des Problems
In der offiziellen Dokumentation von MySQL heißt es eindeutig, dass verschachtelte Transaktionen nicht unterstützt werden:
Transactions cannot be nested. This is a consequence of the implicit commit performed for any current transaction when you issue a START TRANSACTION statement or one of its synonyms.
Aber wenn wir ein komplexes System entwickeln, ist es unvermeidlich, dass wir dies tun Tun Sie dies versehentlich. In der Transaktion ist beispielsweise eine Transaktion verschachtelt, Funktion A verwendet eine Transaktion und Funktion B hat auch eine Transaktion, sodass eine Transaktion verschachtelt ist. Zu diesem Zeitpunkt sind die Angelegenheiten von A eigentlich von geringer Bedeutung. Es wird im obigen Dokument erwähnt. Eine einfache Übersetzung lautet:
Wenn eine START TRANSACTION-Anweisung ausgeführt wird, wird implizit eine Commit-Operation ausgeführt.
Wir müssen also die Verschachtelung von Transaktionen auf der Ebene der Systemarchitektur unterstützen. Glücklicherweise unterstützen einige ausgereifte ORM-Frameworks die Verschachtelung, beispielsweise Doctrine oder Laravel. Schauen wir uns als Nächstes an, wie diese beiden Frameworks implementiert werden.
Freundliche Erinnerung: Die Benennung von Funktionen und Variablen in diesen beiden Frameworks ist relativ intuitiv. Obwohl sie sehr lang aussieht, können Sie die Bedeutung der Funktion oder Variablen direkt durch die Benennung erkennen, also hatte ich keine Angst als ich so ein großes Durcheinander sah :)
2. Doctrines Lösung
Werfen wir zunächst einen Blick auf den Code zum Erstellen einer Transaktion in Doctrine (irrelevanter Code entfernt):
publicfunctionbeginTransaction(){ ++$this->_transactionNestingLevel; if ($this->_transactionNestingLevel == 1) { $this->_conn->beginTransaction(); } elseif ($this->_nestTransactionsWithSavepoints) { $this->createSavepoint($this->_getNestedTransactionSavePointName()); } }
Die erste Zeile dieser Funktion verwendet einen _transactionNestingLevel, um die aktuelle Verschachtelungsebene zu identifizieren, d. h. es gibt noch keine Verschachtelung, dann verwenden Sie die Standardmethode, um START TRANSACTION auszuführen, und es ist in Ordnung. Wenn es größer als 1 ist, das heißt, wenn es eine Verschachtelung gibt, hilft sie uns beim Erstellen eines Sicherungspunkts. Dieser Sicherungspunkt kann als Transaktionsaufzeichnungspunkt verstanden werden. Wenn ein Rollback erforderlich ist, können Sie nur zu diesem Punkt zurückkehren.
Dann schauen Sie sich die RollBack-Funktion an:
publicfunctionrollBack(){ if ($this->_transactionNestingLevel == 0) { throw ConnectionException::noActiveTransaction(); } if ($this->_transactionNestingLevel == 1) { $this->_transactionNestingLevel = 0; $this->_conn->rollback(); $this->_isRollbackOnly = false; } elseif ($this->_nestTransactionsWithSavepoints) { $this->rollbackSavepoint($this->_getNestedTransactionSavePointName()); --$this->_transactionNestingLevel; } else { $this->_isRollbackOnly = true; --$this->_transactionNestingLevel; } }
Sie können sehen, dass die Verarbeitungsmethode auch sehr einfach ist. Wenn die Ebene 1 ist, führen Sie ein direktes Rollback durch, andernfalls ein Rollback zum vorherigen Speicherpunkt.
Dann schauen wir uns weiter die Commit-Funktion an:
publicfunctioncommit(){ if ($this->_transactionNestingLevel == 0) { throw ConnectionException::noActiveTransaction(); } if ($this->_isRollbackOnly) { throw ConnectionException::commitFailedRollbackOnly(); } if ($this->_transactionNestingLevel == 1) { $this->_conn->commit(); } elseif ($this->_nestTransactionsWithSavepoints) { $this->releaseSavepoint($this->_getNestedTransactionSavePointName()); } --$this->_transactionNestingLevel; }
Vergiss es, lass es uns ohne Probleme erklären:)
Laravel's Lösung
Die Verarbeitungsmethode von Laravel ist relativ einfach und grob. Schauen wir uns zunächst den Vorgang zum Erstellen einer Transaktion an:
publicfunctionbeginTransaction(){ ++$this->transactions; if ($this->transactions == 1) { $this->pdo->beginTransaction(); } }
Wie fühlen Sie sich? So einfach, oder? Stellen Sie zunächst fest, wie viele Transaktionen es derzeit gibt. Wenn es die erste ist, wird die Transaktion gestartet. Ansonsten wird nichts unternommen. Schauen Sie sich weiterhin die Funktionsweise von RollBack an:
publicfunctionrollBack(){ if ($this->transactions == 1) { $this->transactions = 0; $this->pdo->rollBack(); } else { --$this->transactions; } }
Verstehen Sie? Nur wenn nur eine aktuelle Transaktion vorhanden ist, wird sie wirklich zurückgesetzt, andernfalls wird die Anzahl nur um eins verringert. Aus diesem Grund habe ich gerade gesagt, dass die Verarbeitung von Laravel relativ einfach und grob ist. Es gibt tatsächlich keine echten Transaktionen in der äußersten Schicht. Obwohl sie einfach und grob ist, löst sie das Problem Wenn die innere Ebene eine neue Transaktion erstellt, führt dies zu Festschreibungsproblemen. Das Prinzip ist so: Der Vollständigkeit halber kopieren Sie auch den Commit-Code!
publicfunctioncommit(){ if ($this->transactions == 1) $this->pdo->commit(); --$this->transactions; }
Das Obige ist der Inhalt der verschachtelten Transaktionsimplementierung von MySQL. Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (m.sbmmt.com).