Detaillierte Erläuterung des Implementierungsprinzips der Transaktionsisolationsstufe in MySQL

Detaillierte Erläuterung des Implementierungsprinzips der Transaktionsisolationsstufe in MySQL

Vorwort

Wenn es um Datenbanktransaktionen geht, fallen jedem schnell viele transaktionsbezogene Kenntnisse ein, wie etwa die ACID-Eigenschaften von Transaktionen, Isolationsebenen, gelöste Probleme (Dirty Reads, nicht wiederholbare Reads, Phantom Reads) usw., aber nur wenige Leute verstehen vielleicht wirklich, wie diese Transaktionseigenschaften implementiert werden und warum es vier Isolationsebenen gibt.

Heute werden wir zunächst über das Implementierungsprinzip der Transaktionsisolierung in MySQL sprechen und anschließend Artikel veröffentlichen, in denen wir die Implementierungsprinzipien anderer Funktionen analysieren.

Natürlich ist MySQL umfangreich und tiefgründig, und Auslassungen im Artikel sind unvermeidlich. Kritik und Korrekturen sind willkommen.

veranschaulichen

Die Transaktionsimplementierungslogik von MySQL befindet sich auf der Engine-Ebene und nicht alle Engines unterstützen Transaktionen. Die folgenden Anweisungen basieren auf der InnoDB-Engine.

Definition

Isolation bedeutet, dass nach der Übermittlung und Ausführung verschiedener Transaktionen nacheinander die Endergebnisse seriell sind. Das heißt, dass bei einer Transaktion während ihrer Ausführung nur die Datenänderungen wahrgenommen werden sollten, die durch ihre eigenen Vorgänge verursacht wurden, und dass keine Datenänderungen durch andere Transaktionen verursacht werden sollten.

Durch die Isolierung werden die Probleme gelöst, die durch gleichzeitige Transaktionen entstehen.

Standardmäßige SQL-Isolationsebenen

Die einfachste Möglichkeit zur Implementierung der Isolation besteht darin, jede Transaktion seriell auszuführen. Wenn die vorherige Transaktion nicht abgeschlossen wurde, warten die nachfolgenden Transaktionen. Allerdings weist diese Implementierungsmethode offensichtlich eine geringe Parallelitätseffizienz auf und ist für den Einsatz in tatsächlichen Umgebungen nicht geeignet.

Um die oben genannten Probleme zu lösen und unterschiedliche Grade der Parallelitätskontrolle zu erreichen, haben die SQL-Standardgeber unterschiedliche Isolationsebenen vorgeschlagen: nicht festgeschriebenes Lesen, festgeschriebenes Lesen, wiederholbares Lesen und serialisierbares Lesen. Die höchste Isolationsebene ist das serialisierte Lesen. Da Transaktionen auf anderen Isolationsebenen gleichzeitig ausgeführt werden, sind einige Probleme mehr oder weniger zulässig. Siehe die Matrix unten:

Isolationsstufe (+: erlaubt, -: nicht erlaubt) Schmutzige Lektüre Nicht wiederholbares Lesen Phantom lesen
Nicht festgeschrieben lesen + + +
Lesebestätigung - + +
Wiederholbares Lesen - - +
Serialisiertes Lesen - - -

Beachten Sie, dass die InnoDB-Engine von MySQL das Problem nicht wiederholbarer Lesevorgänge durch MVCC auf der Ebene des festgeschriebenen Lesens löst und das Problem von Phantom-Lesevorgängen durch Lückensperren auf der Ebene wiederholbarer Lesevorgänge löst. Weitere Einzelheiten finden Sie in der Analyse weiter unten.

Umsetzungsprinzip

Implementierungsprinzip der Standard-SQL-Transaktionsisolationsebene

Das oben angesprochene Problem ist eigentlich ein Kontrollproblem bei gleichzeitigen Transaktionen. Die häufigste Methode zur Lösung gleichzeitiger Transaktionen ist die pessimistische Parallelitätskontrolle (dh Sperren in der Datenbank). Die Implementierung von Standard-SQL-Transaktionsisolationsebenen basiert auf Sperren. Sehen wir uns an, wie dies implementiert wird:

Transaktionsisolationsebene Durchführung
Nicht festgeschriebenes Lesen (RU) Die Transaktion sperrt die aktuell gelesenen Daten nicht;

Wenn eine Transaktion bestimmte Daten aktualisiert (also in dem Moment, in dem die Aktualisierung erfolgt), muss diesen zunächst eine gemeinsame Sperre auf Zeilenebene hinzugefügt werden, die erst nach Abschluss der Transaktion aufgehoben wird.
Lesen bestätigt (RC) Die Transaktion fügt den aktuell gelesenen Daten eine gemeinsame Sperre auf Zeilenebene hinzu (nur beim Lesen gesperrt) und gibt die gemeinsame Sperre auf Zeilenebene unmittelbar nach dem Lesen der Zeile frei.

Wenn eine Transaktion bestimmte Daten aktualisiert (d. h. in dem Moment, in dem die Aktualisierung erfolgt), muss ihr zuerst eine exklusive Sperre auf Zeilenebene hinzugefügt werden, die erst nach Abschluss der Transaktion aufgehoben wird.
Wiederholbares Lesen (RR) Wenn eine Transaktion ein Datenelement liest (d. h. in dem Moment, in dem sie mit dem Lesen beginnt), muss sie zunächst eine gemeinsame Sperre auf Zeilenebene hinzufügen, die erst nach Abschluss der Transaktion aufgehoben wird.

Wenn eine Transaktion bestimmte Daten aktualisiert (d. h. in dem Moment, in dem die Aktualisierung erfolgt), muss ihr zuerst eine exklusive Sperre auf Zeilenebene hinzugefügt werden, die erst nach Abschluss der Transaktion aufgehoben wird.
Serialisiertes Lesen (S) Wenn eine Transaktion Daten liest, muss sie diesen zunächst eine gemeinsame Sperre auf Tabellenebene hinzufügen, die erst nach Abschluss der Transaktion aufgehoben wird.

Wenn eine Transaktion Daten aktualisiert, muss sie diesen zunächst eine exklusive Sperre auf Tabellenebene hinzufügen, die erst nach Abschluss der Transaktion aufgehoben wird.

Es ist ersichtlich, dass, wenn nur Sperren verwendet werden, um die Steuerung der Isolationsstufe zu implementieren, häufiges Sperren und Entsperren erforderlich ist und wahrscheinlich Lese-/Schreibkonflikte auftreten (beispielsweise aktualisiert auf RC-Ebene Transaktion A Datenzeile 1 und Transaktion B liest Datenzeile 1, bevor Transaktion A festschreibt, und muss warten, bis Transaktion A festschreibt und die Sperre freigibt).

Um das Problem von Lese-/Schreibkonflikten ohne Sperren zu lösen, hat MySQL den MVCC-Mechanismus eingeführt. Einzelheiten finden Sie in meinem vorherigen Analyseartikel: Verstehen Sie optimistisches Sperren, pessimistisches Sperren und MVCC in der Datenbank in einem Artikel.

Implementierungsprinzip der InnoDB-Transaktionsisolationsebene

Bevor wir fortfahren, müssen wir einige Konzepte verstehen:

1. Sperrendes Lesen und konsistentes nicht sperrendes Lesen

Lesezugriff sperren: Lesezugriffe in einer Transaktion aktiv sperren, wie z. B. „SELECT ... LOCK IN SHARE MODE“ und „SELECT ... FOR UPDATE“. Es werden jeweils gemeinsam genutzte Zeilensperren und exklusive Zeilensperren hinzugefügt. Die Klassifizierung der Sperren finden Sie in meinem vorherigen Analyseartikel: MySQL-Sperrklassifizierung, die Sie kennen sollten).

https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html

Konsistentes Lesen ohne Sperren: InnoDB verwendet MVCC, um der Abfrage der Transaktion zu einem bestimmten Zeitpunkt einen Datenbank-Snapshot bereitzustellen. Die Abfrage erkennt Änderungen, die durch vor diesem Zeitpunkt festgeschriebene Transaktionen vorgenommen wurden, nicht jedoch Änderungen, die durch spätere oder nicht festgeschriebene Transaktionen (mit Ausnahme dieser Transaktion) vorgenommen wurden. Das heißt, nachdem die Transaktion gestartet wurde, sind die von der Transaktion angezeigten Daten alle Daten zum Zeitpunkt des Transaktionsstarts, und nachfolgende Änderungen anderer Transaktionen sind in dieser Transaktion nicht sichtbar.

Konsistentes Lesen ist der Standardmodus für InnoDB, um SELECT-Anweisungen auf den Isolationsebenen RC und RR zu verarbeiten. Bei einem konsistenten Lesevorgang ohne Sperren werden für die Tabellen, auf die zugegriffen wird, keine Sperren festgelegt. Daher können andere Transaktionen die Tabellen gleichzeitig lesen oder ändern, während für die Tabellen ein konsistenter Lesevorgang ohne Sperren ausgeführt wird.

https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html

2. Aktueller Lesevorgang und Snapshot-Lesvorgang

Aktueller Messwert

Gelesen wird die neueste Version. Operationen wie UPDATE, DELETE, INSERT, SELECT ... LOCK IN SHARE MODE und SELECT ... FOR UPDATE sind alles aktuelle Lesevorgänge. Warum werden sie aktuelle Lesevorgänge genannt? Das heißt, es liest die neueste Version des Datensatzes. Beim Lesen muss sichergestellt werden, dass andere gleichzeitige Transaktionen den aktuellen Datensatz nicht ändern können und der gelesene Datensatz gesperrt wird.

Snapshot lesen

Gelesen wird die Snapshot-Version, also die historische Version. Beispielsweise ist eine entsperrte SELECT-Operation ein Snapshot-Lesen, also ein entsperrtes, nicht blockierendes Lesen. Die Voraussetzung für ein Snapshot-Lesen ist, dass die Isolationsebene weder eine nicht festgeschriebene Leseebene noch eine serialisierte Leseebene ist, da ein nicht festgeschriebenes Lesen immer die neueste Datenzeile liest, nicht die Datenzeile, die der aktuellen Transaktionsversion entspricht, und ein serialisiertes Lesen die Tabelle sperrt.

3. Implizite Sperrung und explizite Sperrung

Implizite Sperrung

InnoDB verwendet während der Transaktionsausführung ein zweiphasiges Sperrprotokoll (ohne aktiv eine explizite Sperre durchzuführen):

  • Die Sperrung kann jederzeit durchgeführt werden, und InnoDB führt die Sperrung bei Bedarf entsprechend der Isolationsstufe automatisch durch.
  • Die Sperre wird nur aufgehoben, wenn ein Commit oder Rollback ausgeführt wird, und alle Sperren werden gleichzeitig aufgehoben.

Explizite Sperrung

  • InnoDB unterstützt auch explizites Sperren durch bestimmte Anweisungen (Speicher-Engine-Ebene)
select ... lock im Share-Modus //Gemeinsame Sperre select ... für Update //Exklusive Sperre
  • Anzeigesperren auf MySQL-Serverebene:
Sperrtabelle
Tabelle entsperren

Nachdem wir die oben genannten Konzepte verstanden haben, schauen wir uns an, wie InnoDB-Transaktionen implementiert werden (die folgenden Ausführungen beziehen sich auf nicht aktiv gesperrte Auswahlen).

Transaktionsisolationsebene Durchführung
Nicht festgeschriebenes Lesen (RU) Die Transaktion sperrt die aktuell gelesenen Daten nicht und alle Daten werden aktuell gelesen.

Wenn eine Transaktion bestimmte Daten aktualisiert (also in dem Moment, in dem die Aktualisierung erfolgt), muss diesen zunächst eine gemeinsame Sperre auf Zeilenebene hinzugefügt werden, die erst nach Abschluss der Transaktion aufgehoben wird.
Lesen bestätigt (RC) Die Transaktion sperrt die aktuell gelesenen Daten nicht und ist ein Snapshot-Lesevorgang.

Wenn eine Transaktion bestimmte Daten aktualisiert (d. h. in dem Moment, in dem die Aktualisierung erfolgt), muss sie diesen zunächst eine exklusive Sperre auf Zeilenebene (Datensatz) hinzufügen, die erst am Ende der Transaktion aufgehoben wird.

Durch Snapshots löst MySQL das Problem nicht wiederholbarer Lesevorgänge auf dieser Ebene.
Wiederholbares Lesen (RR) Die Transaktion sperrt die aktuell gelesenen Daten nicht und ist ein Snapshot-Lesevorgang.

Wenn eine Transaktion bestimmte Daten aktualisiert (d. h. in dem Moment, in dem die Aktualisierung erfolgt), muss sie diesen Daten zunächst eine exklusive Sperre auf Zeilenebene (Record, GAP, Next-Key) hinzufügen, die erst am Ende der Transaktion aufgehoben wird.

Durch Gap-Locks löst MySQL das Problem der Phantom-Lesevorgänge auf dieser Ebene.
Serialisiertes Lesen (S) Wenn eine Transaktion Daten liest, muss sie zuerst eine gemeinsame Sperre auf Tabellenebene hinzufügen, die erst freigegeben wird, wenn die Transaktion beendet ist. Dies ist alles aktuelle Lesen.

Wenn eine Transaktion Daten aktualisiert, muss sie diesen zunächst eine exklusive Sperre auf Tabellenebene hinzufügen, die erst nach Abschluss der Transaktion aufgehoben wird.

Wie Sie sehen, löst InnoDB das Problem von Lese-/Schreibkonflikten durch MVCC sehr gut und löst die Probleme von Phantomlesevorgängen und nicht wiederholbaren Lesevorgängen, die auf der Standardebene auftreten, eine Ebene im Voraus, wodurch die Parallelitätsfähigkeit der Datenbank erheblich verbessert wird.

Einige häufige Missverständnisse

Ist beim Phantomlesen auch das Löschen enthalten?

Nicht wiederholbares Lesen: Eine Zeile wird vor und nach dem Lesen mehrfach gelesen, der Dateninhalt ist inkonsistent und es handelt sich um eine Aktualisierungs- und Löschoperation für andere Transaktionen. Um dieses Problem zu lösen, verwenden Sie gemeinsam genutzte Zeilensperren, um bis zum Ende der Transaktion zu sperren (dh auf RR-Ebene. Natürlich verwendet MySQL MVCC, um dieses Problem auf RC-Ebene zu lösen).

Phantom-Lesevorgänge: Phantom-Lesevorgänge treten auf, wenn dieselbe Abfrage zu unterschiedlichen Zeiten unterschiedliche Zeilensätze generiert. Dies wird durch Einfügevorgänge anderer Transaktionen verursacht. Um dieses Problem zu lösen, sperren Sie die gesamte Tabelle bis zum Ende der Transaktion (d. h. auf S-Ebene). Natürlich verwendet MySQL Lückensperren auf RR-Ebene, um dieses Problem zu lösen.

In vielen Artikeln im Internet werden Phantom-Lesevorgänge und Commit-Lesevorgänge erwähnt. Einige sagen, dass Phantom-Lesevorgänge auch Löschungen beinhalten, während andere sagen, dass Löschungen als Commit-Lesevorgangsproblem betrachtet werden sollten. Was ist also die Wahrheit? Schauen wir uns die offizielle Dokumentation von MySQL an (wie folgt)

Das sogenannte Phantomproblem tritt innerhalb einer Transaktion auf, wenn dieselbe Abfrage zu verschiedenen Zeitpunkten unterschiedliche Zeilensätze erzeugt. Wenn beispielsweise ein SELECT zweimal ausgeführt wird, beim zweiten Mal jedoch eine Zeile zurückgibt, die beim ersten Mal nicht zurückgegeben wurde, ist die Zeile eine „Phantomzeile“.
https://dev.mysql.com/doc/refman/5.7/en/innodb-next-key-locking.html

Es ist ersichtlich, dass Phantom-Lesevorgänge auf Änderungen vor und nach dem Ergebnissatz abzielen, sodass die Löschsituation anscheinend als Phantom-Lesevorgang klassifiziert werden sollte. Wenn wir jedoch das Implementierungsprinzip des oben aufgeführten Standard-SQL auf RR-Ebene tatsächlich analysieren, wissen wir, dass die RR-Ebene des Standard-SQL den gefundenen Datenzeilen eine Zeilenfreigabesperre hinzufügt, sodass andere Transaktionen diese Datenzeilen zu diesem Zeitpunkt nicht löschen können. Daher wird es unter RR aufgrund des Löschens keinen Phantom-Lesevorgang geben, d. h. der Phantom-Lesevorgang umfasst kein Löschen.

Kann MVCC das Phantomleseproblem lösen?

In vielen Artikeln im Internet heißt es, dass MVCC oder MVCC+Gap-Locks das Phantom-Read-Problem lösen. Tatsächlich kann MVCC das Phantom-Read-Problem jedoch nicht lösen. Zum Beispiel:

beginnen;

#Angenommen, die Benutzertabelle ist leer, sind die unten gefundenen Daten leer. select * from users; #Keine Sperre #Zu diesem Zeitpunkt wird eine andere Transaktion festgeschrieben und Daten mit der ID=1 eingefügt. select * from users; #Lesen Sie den Snapshot, die gefundenen Daten sind leer. update users set name='mysql' where id=1; #update ist das aktuelle Lesen, daher ist das Update erfolgreich und ein aktualisierter Snapshot wird generiert. select * from users; #Lesen Sie den Snapshot und suchen Sie einen Datensatz mit der ID 1, da MVCC den durch das aktuelle Transaktions-Commit generierten Snapshot finden kann.

Es ist ersichtlich, dass die vorher und nachher überprüften Datenzeilen inkonsistent sind und Phantom-Lesevorgänge aufgetreten sind. Daher kann MVCC allein das Phantomleseproblem nicht lösen. Die Lösung des Phantomleseproblems hängt von Gap Locks ab. wie folgt:

beginnen;

#Angenommen, die Benutzertabelle ist leer. Die unten gefundenen Daten sind leer. Select * from users lock im Share-Modus; #Gemeinsame Sperre hinzufügen #Zu diesem Zeitpunkt möchte eine andere Transaktion B ein Commit ausführen und fügt Daten mit der ID=1 ein. Aufgrund der Lückensperre muss sie warten. Select * from users; #Snapshot lesen. Die gefundenen Daten sind leer. Update users set name='mysql' where id=1; #Update ist der aktuelle Lesevorgang, und es wird kein Update durchgeführt, da keine Daten vorhanden sind. Select * from users; #Snapshot lesen. Die gefundenen Daten sind leer. Commit;

#Transaktion B wurde erfolgreich übermittelt und Daten werden eingefügt

Beachten Sie, dass wir es zum Lösen des Phantomleseproblems auf RR-Ebene explizit sperren müssen, da es sonst während der Abfrage nicht gesperrt wird.

Oben finden Sie eine ausführliche Erläuterung des Implementierungsprinzips der Transaktionsisolationsebenen in MySQL. Weitere Informationen zu MySQL-Transaktionsisolationsebenen finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • So wählen Sie die Transaktionsisolationsebene in einem MySQL-Projekt
  • Detaillierte Erläuterung der Transaktionsisolationsebenen in den MySql-Studiennotizen
  • Beschreibung der Standardtransaktionsisolationsebene von MySQL und Oracle
  • Beschreiben Sie kurz die vier Transaktionsisolationsebenen von MySql
  • MySQL-Fallanalyse der Transaktionsisolationsebene

<<:  Implementierung des Imports und Exports von Docker-Images

>>:  CSS-Methode zur Steuerung der Elementhöhe von unten nach oben und von oben nach unten

Artikel empfehlen

Ausführliches Tutorial zu Installations- und Upgradeproblemen bei MySQL 5.7.30

Keil Da auf dem Computer eine relativ alte MySQL-...

So zeigen Sie JSON-Daten in HTML an

Hintergrund: Manchmal müssen wir JSON-Daten direk...

Beispiele für die Implementierung und Verwendung von geplanten MySQL-Aufgaben

Dieser Artikel veranschaulicht anhand von Beispie...

So ändern Sie den Bildlaufleistenstil in Vue

Inhaltsverzeichnis Zunächst müssen Sie wissen, da...

So halten Sie eine lange Verbindung aufrecht, wenn Sie den Nginx-Reverse-Proxy verwenden

· 【Szenenbeschreibung】 Nach HTTP1.1 unterstützt d...

MariaDB-Remote-Login-Konfiguration und Problemlösung

Vorwort: Der Installationsvorgang wird nicht im D...

Lösung zum Vergessen des Passworts des Pagodenpanels in Linux 3.X/4.x/5.x

Geben Sie ssh ein und geben Sie den folgenden Bef...

So stellen Sie HBase mit Docker bereit

Standalone-HBase, lassen Sie uns zuerst darüber s...

Eine einfache Methode zum Ändern der Größe hochgeladener Nginx-Dateien

Originallink: https://vien.tech/article/138 Vorwo...

Detaillierte Erklärung dieser Referenz in React

Inhaltsverzeichnis Ursache: durchlaufen: 1. Konst...