Inhaltsverzeichnis- 1. Schloss und Riegel
- 2. Wiederholbares Lesen
- 3. Verriegelungsvorgang einfügen
- 3.1 Sperrmodus
- 3.2 Verriegelungsvorgang
- 3.3 Implizite Sperre
- 4. Sperrvorgang auswählen
Prämisse dieses Artikels: Code MySQL 8.0.13 Nur der aktuelle Lesevorgang des Repeatable Read wird sortiert. Read Committed ist viel einfacher. Darüber hinaus basieren Snapshot-Lesevorgänge auf MVCC und erfordern keine Sperrung. Sie werden daher in diesem Artikel nicht behandelt. 1. Schloss und Riegel lock in InnoDB ist eine Sperre, die dem in der Transaktion aufgerufenen/geänderten record hinzugefügt wird und im Allgemeinen aufgehoben wird, wenn die Transaktion festgeschrieben oder zurückgesetzt wird. Latch ist eine Sperre, die Btree-Seiten hinzugefügt wird, wenn record auf BTree gesucht werden. Normalerweise sperrt es den entsprechenden record auf der Seite und gibt ihn nach dem Zugriff/der Änderung frei. Der Sperrbereich von Latch ist viel kleiner als der von Lock. In der konkreten Implementierung wird eine große transaction in mehrere kleine mini transaction(mtr ) aufgeteilt, wie in der folgenden Abbildung dargestellt: Es gibt eine transaction , die nacheinander die Operationen insert , select…for update und update ausführt. Diese drei Operationen entsprechen jeweils drei mtr. Jede mtr führt Folgendes aus: - Suchen Sie den
record im B-Baum und fügen Sie關page latch hinzu. -
record lock hinzufügen und entsprechenden record ändern -
page latch
lösen page latch
Warum das tun? Aus Gründen der Parallelität wurde für jede Operation in der Transaktion nach Abschluss von Schritt 2 der entsprechende record durch eine Sperre geschützt, um sicherzustellen, dass andere gleichzeitige Transaktionen ihn nicht ändern können. Daher ist es zu diesem Zeitpunkt nicht erforderlich, page latch zu belegen, auf dem sich record befindet. Andernfalls bleiben andere Transaktionen, wenn sie auf verschiedene record derselben page zugreifen/diese ändern, was parallel erfolgen kann, hier durch page latch hängen. 
Die Sperre wird在lock_sys->rec_hash .個record lock wird in rec_hash durch <space_id , page_no , heap_no> identifiziert. latch befindet sich im block bufferpool , der page entspricht, entsprechend block->lock Dieser Artikel konzentriert sich nur auf Dinge, die mit Schlössern zu tun haben. Der Riegel wird später in einem separaten Artikel behandelt. 2. Wiederholbares Lesen Ich werde nicht auf jede Isolationsebene im Detail eingehen. Hier werde ich hauptsächlich über RR sprechen. Wie der Name schon sagt, unterstützt RR Wiederholbarkeit, d. h. in einer Transaktion sollten mehrere Ausführungen desselben SELECT…FOR UPDATE denselben Ergebnissatz ergeben (mit Ausnahme der von dieser Transaktion vorgenommenen Änderungen). Dies erfordert, dass keine andere Transaktion neue Datensätze in das SELECT-Intervall einfügen kann. Daher muss SELECT zusätzlich zum Sperren der Datensätze, die die Bedingungen erfüllen, auch das entsprechende Intervall sperren, um es zu schützen. In der Implementierung von InnoDB gibt es keine Sperre, die ein bestimmtes Intervall auf einmal sperrt. Stattdessen wird eine große Intervallsperre aufgeteilt und auf mehrere Datensätze gesetzt, die sich bereits im Intervall befinden. Aus diesem Grund werden die Konzepte Gap Lock und Next-Key Lock eingeführt. Sie werden einem bestimmten Datensatz hinzugefügt. -
Gap lock schützt das offene Intervall zwischen diesem Datensatz und dem vorherigen Datensatz. -
Next-key lock schützt das links geöffnete und rechts geschlossene Intervall zwischen diesem Datensatz und dem vorherigen Datensatz.
Sie alle dienen dazu, dieses Intervall davor zu schützen, von anderen Transaktionen in neue Datensätze eingefügt zu werden, und implementieren so RR. Als nächstes schauen wir uns an, wie Insert und Select von der Quellcodeimplementierung ausgeschlossen werden. Indem wir sie kombinieren, können wir verstehen, wie InnoDBs RR implementiert wird. Einfügesperren sind über den gesamten Einfügevorgang und in mehreren damit verbundenen Funktionen verteilt, während Auswahlsperren stärker in在row_search_mvcc konzentriert sind. 3. Verriegelungsvorgang einfügen 3.1 Sperrmodus Die Sperrmodi sind hauptsächlich Share (S) und Exclusive (X) [entspricht LOCK_S und LOCK_X im Code] Zu den Lückensperrmodi gehören hauptsächlich Datensatzsperre, Lückensperre und Nächste-Tasten-Sperre [entspricht LOCK_REC_NOT_GAP, LOCK_GAP, LOCK_ORDINARY im Code]. In der Praxis ist der tatsächliche Sperrtyp after mode|gap_mode. Eine Datensatzsperre ist eine Datensatzsperre, die auf einen einzelnen Datensatz angewendet wird. Obwohl Gap lock/Next-key lock auch auf einen bestimmten Datensatz angewendet wird, wird sie verwendet, um sicherzustellen, dass keine anderen gleichzeitigen Transaktionen in die Lücke vor dem Datensatz einfügen. Wie wird dies implementiert? InnoDB führt eine Einfügeabsichtssperre ein, deren tatsächlicher Typ ist (LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION)
Gegenseitig ausschließend mit Gap lock/Next-key lock . Wenn vor dem Einfügen eine Sperre für den nächsten Datensatz an der Einfügeposition erkannt wird, wird dem nächsten Datensatz eine Einfügeabsichtssperre hinzugefügt, die anzeigt, dass die Transaktion beabsichtigt, einen neuen Datensatz in die Lücke einzufügen. Wenn eine andere Transaktion hier bereits Gap/Next-key lock gesetzt hat, bedeutet das, dass sie diese Stelle schützen möchte. Daher muss die aktuelle Sperre der Einfügungsabsicht warten, bis die entsprechende Transaktion festgeschrieben ist. Diese Erkennung erfolgt nur in eine Richtung, d. h. die Gap/Next-key lock , aber keine Sperre muss auf die Freigabe der Sperre für die Einfügeabsicht warten, da sonst die Parallelität nicht in Konflikt stehender Einfügevorgänge in dieser Lücke ernsthaft beeinträchtigt wird. Die spezifische Erkennung von Sperrkonflikten erfolgt in der Funktion lock_rec_has_to_wait. Das allgemeine Prinzip lautet: Um festzustellen, ob zwei Sperren kompatibel oder inkompatibel sind, führen Sie zunächst eine Moduskonflikterkennung durch

Wenn kein Konflikt vorliegt, bedeutet dies, dass die Sperren kompatibel sind und nicht gewartet werden muss. Wenn ein Konflikt vorliegt, wird die Konfliktausnahmeerkennung im Lückenmodus durchgeführt, die wie folgt zusammengefasst wird: 
Wenn es zwischen den Gap-Modi nicht zu Konflikten kommt, werden die Sperren ausnahmsweise als kompatibel betrachtet und es ist keine Wartezeit erforderlich. Sie können sehen: - Das Einfügen der Absichtssperre erfordert das Warten auf
Gap lock und Next-key lock - Jedes Schloss muss nicht auf das Einfügen des Absichtsschlosses warten
-
Gap lock muss nicht auf ein Schloss warten -
Next-key lock muss auf andere Next-key lock及Record Lock warten und umgekehrt
Nachdem wir nun diese Prinzipien der Sperrkompatibilität verstehen, können wir sehen, wie sie im eigentlichen Einfügevorgang verwendet werden. 3.2 Verriegelungsvorgang Die Insert besteht darin, zuerst den Primärschlüsselindex und dann nacheinander den Sekundärindex einzufügen. Nachfolgend ist der Vorgang aus dem Code herausgearbeitet, der Vorgang des Einfügens eines entry , Für den Primärschlüsselindex: (1) Durchsuchen Sie zuerst den B-Baum, fügen Sie den entsprechenden page latch hinzu und suchen Sie record (<= entry) (2) Wenn der einzufügende Eintrag bereits vorhanden ist, d. h. entry = record , wird Folgendes beurteilt: - Wenn es sich um
INSERT ON DUPLICATE KEY UPDATE handelt, fügen Sie record X Next-key lock hinzu - Wenn es sich um ein normales
INSERT handelt, fügen Sie record S Next-key lock hinzu
Stellen Sie dann fest, ob es sich bei dem Datensatz um eine gelöschte Markierung handelt: - Wenn es sich nicht um eine Löschmarkierung handelt, bedeutet dies, dass tatsächlich ein Duplikat vorhanden ist,
DB_DUPLICATE_KEY wird an die obere Ebene zurückgegeben. Die obere Ebene bestimmt dann, ob der Vorgang in ein Update umgewandelt oder dem Benutzer ein Duplikatsfehler gemeldet werden soll, je nachdem, ob es sich um INSERT ON DUPLICATE KEY UPDATE oder ein normales INSERT handelt. - Wenn es sich um eine gelöschte Markierung handelt, bedeutet dies, dass es tatsächlich keinen
duplicate record gibt. Gehen Sie also nach unten
(3) Bestimmen Sie, ob für den nächsten Eintrag des Datensatzes eine Sperre vorhanden ist. Wenn ja, fügen Sie eine Sperre für die Einfügungsabsicht hinzu, um sicherzustellen, dass in dem Intervall, in dem der Eintrag eingefügt werden soll, kein anderer Gap lock/Next-key lock Schutz vorhanden ist. (4) Eintrag einfügen (5) page latch lösen, der die Sperre noch immer festhält Für sekundäre Indizes (1) Durchsuchen Sie zuerst den B-Baum, fügen Sie den entsprechenden Seitenriegel hinzu und suchen Sie record (<= entry) (2) Wenn der einzufügende Eintrag bereits vorhanden ist, das heißt, entry = record , und der aktuelle Index eindeutig ist: - Wenn es sich um
INSERT ON DUPLICATE KEY UPDATE handelt, fügen Sie record X Next-key lock hinzu - Wenn es sich um ein normales INSERT handelt, fügen Sie
S Next-key lock zu record2 hinzu
Ermitteln, ob Datensatz und Eintrag gleich sind: Wenn sie gleich sind und es sich um ein normales INSERT handelt, bestimmen Sie, ob der Datensatz eine gelöschte Markierung ist: - Wenn es sich nicht um eine Löschmarkierung handelt, bedeutet dies, dass tatsächlich ein Duplikat vorhanden ist,
DB_DUPLICATE_KEY wird an die obere Ebene zurückgegeben. Die obere Ebene bestimmt dann, ob der Vorgang in ein Update umgewandelt oder dem Benutzer ein Duplikatsfehler gemeldet werden soll, je nachdem, ob es sich um INSERT ON DUPLICATE KEY UPDATE還 ein normales INSERT handelt. - Wenn es sich um eine Löschmarkierung handelt, gibt es tatsächlich kein Duplikat. Gehen Sie also nach unten
(3) Wenn es sich um INSERT ON DUPLICATE KEY UPDATE handelt und der aktuelle Index eindeutig ist, wird für die nächste Transaktion eine record X Gap lock gesetzt, um zu verhindern, dass der gleiche Eintrag von anderen Transaktionen eingefügt wird. (4) Bestimmen Sie, ob für den nächsten Datensatz des Datensatzes eine Sperre vorhanden ist. Wenn ja, fügen Sie ihm eine Sperre für die Einfügeabsicht hinzu. (LOCK_X | LOCK_GAP | LOCK_INSERT_INTENTION)
Stellen Sie sicher, dass der Bereich, in dem Sie den Eintrag einfügen möchten, nicht durch andere Gap lock/Next-key lock (5) Eintrag einfügen (6) Seitenverriegelung lösen Hinweis : Schritt 3 von [Sekundärindex] scheint redundant, denn selbst wenn andere gleichzeitige Transaktionen INSERT ON DUPLICATE KEY UPDATE verwenden, um denselben Datensatz einzufügen, kann Schritt 1 nur seriell aufgerufen werden, genau wie der Prozess [Primärschlüsselindex]. Der erste Thread findet nicht denselben Datensatz wie der Eintrag und geht daher zu Schritt 4 über, um ihn einzufügen. Der zweite Thread kann Schritt 1 erst aufrufen, nachdem Schritt 6 beendet und die Seitenverriegelung freigegeben wurde. Zu diesem Zeitpunkt bleibt er in Schritt 2 an X Next-key lock des hinzugefügten Datensatzes hängen und kann erst fortfahren, nachdem die Transaktion von Thread 1 festgeschrieben wurde. Es scheint also, dass es keinen Konflikt geben wird? Der obige Prozess befindet sich in der Funktion row_ins_index_entry und der spezifische Eintrag lautet wie folgt:
mysql_parse->mysql_execute_command->Sql_cmd_dml::execute->
Sql_cmd_insert_values::execute_inner->write_record->handler::ha_write_row->
ha_innobase::Zeile_schreiben->Zeile_für_MySQL_einfügen->Zeile_für_MySQL_mit_Ins_Graph_einfügen->
Zeile_Einfügungsschritt->Zeile_Einfügungsschritt->Zeile_Einfügungsindex_Einfügungsschritt->Zeile_Einfügungsindex
Die Sperre für die Einfügeabsicht wird in der Funktion lock_rec_insert_check_and_lock hinzugefügt und der Eintrag lautet wie folgt:
row_ins_index_eintrag->row_ins_cluster_index_eintrag/row_ins_sec_index_eintrag->
btr_cur_optimistic_insert/btr_cur_pessimistic_insert->btr_cur_ins_lock_and_undo->
lock_rec_insert_check_and_lock
3.3 Implizite Sperre Ein weiterer zu erwähnender Punkt ist, dass der Einfügevorgang nicht explizit gesperrt wird. Jeder Einfügedatensatz hat standardmäßig eine implizite Sperre, die durch das versteckte Feld trx_id des Datensatzes erkannt wird. Für den Primärschlüsselindex muss, wenn der einzufügende Datensatz im B-Baum gefunden wird, nur die trx_id des vorhandenen Datensatzes verglichen werden. Wenn die dieser trx_id entsprechende Transaktion noch eine aktive Transaktion ist, bedeutet dies, dass die Einfügetransaktion dieses Datensatzes nicht festgeschrieben wurde. Dies bedeutet implizit, dass für diesen Datensatz eine Sperre vorliegt. Zu diesem Zeitpunkt wird sie in eine explizite Sperre umgewandelt und in lock_sys eingefügt und wartet. Dies geschieht, um die Leistung zu verbessern und Operationen an lock_sys zu minimieren. Die implizite Sperrerkennung für sekundäre Indizes ist nicht so einfach wie für Primärschlüsselindizes, da sekundäre Indexdatensätze keine trx_id aufzeichnen. Wir können nur max_trx_id auf der Seite, auf der sie sich befindet, mit der minimalen trx_id in der aktuell aktiven Transaktionsliste vergleichen. Wenn sie kleiner als die max_trx_id ist, bedeutet dies, dass die letzte Transaktion, die diese Seite geändert hat, festgeschrieben wurde, sodass keine implizite Sperre für den Datensatz vorliegt. Wenn sie größer oder gleich der max_trx_id ist, müssen wir zum Primärschlüssel zurückkehren, um den entsprechenden Primärschlüsseldatensatz zu finden, und die Undo-Historie durchlaufen, um zu bestätigen, ob eine implizite Sperre vorliegt. Die spezifische Implementierung befindet sich in row_vers_impl_x_locked_low . 4. Sperrvorgang auswählen Der Sperrvorgang für SELECT zum aktuellen Lesen erfolgt in row_search_mvcc. Eine SELECT-Anweisung wird diese Funktion mehrmals aufrufen. Das erste Mal erfolgt über index_read->row_search_mvcc , was normalerweise der erste Zugriff auf den Index ist, um den genauen Datensatz in WHERE zu finden. Jeder nachfolgende Zugriff erfolgt über general_fetch->row_search_mvcc , und prev/next record wird gemäß bestimmten Bedingungen durchlaufen, bis alle Datensätze abgerufen wurden, die die WHERE-Bedingungen erfüllen. Die spezifische Sperre wird während des Zugriffs auf Datensätze und beim Durchlaufen dieser ausgeführt. Der Code row_search_mvcc ist sehr lang. Hier werde ich nur den Sperrvorgang zusammenfassen: - Suchen Sie den Datensatz, der dem Suchtupel im Index entspricht. (Der Datensatz hier kann die erste Suche nach dem Datensatz sein, der
search_tuple durch den oben erwähnten Index-B-Baum entspricht, oder es kann sich um den vorherigen/nächsten Datensatz handeln, der durch Wiederherstellen der letzten Zugriffsposition durch den zuvor gespeicherten Cursor nach mehreren General_Fetches erhalten wurde.) - Wenn es index_read ist und der Modus
PAGE_CUR_L oder PAGE_CUR_LE ist, fügen Sie GAP LOCK zum nächsten Datensatz des gefundenen Datensatzes hinzu - Wenn der Datensatz Infimum ist, springen Sie zu Schritt 9 next_rec. Wenn er Supremum ist, fügen Sie Next-Key Lock hinzu und springen Sie zu Schritt 9 next_rec.
- Wenn es index_read ist, ist der Datensatz nicht gleich search_tuple, füge
GAP LOCK zum record hinzu und gib NOT FOUND zurück - Hier zeigen wir, dass der Datensatz gleich dem Suchtupel ist, und fügen dem Datensatz die Next-Key-Sperre hinzu. Mit zwei Ausnahmen wird nur die Rec-Sperre hinzugefügt:
- Für index_read: Wenn der aktuelle Index ein Primärschlüsselindex ist und
mode PAGE_CUR_GE ist und die Anzahl der Felder in search_tuple gleich der Anzahl der eindeutigen Felder im Index ist, - Überprüfen Sie, ob es sich um eine eindeutige Suche handelt, d. h. die Anzahl der Felder im Suchtupel entspricht der Anzahl der eindeutigen Felder im aktuellen Index und der aktuelle Index ist ein Primärschlüsselindex oder (es ist ein Sekundärindex und das Suchtupel enthält keine NULL-Felder) und der Datensatz ist keine gelöschte Markierung.
- Dies zeigt an, dass die Sperre erfolgreich war. Anschließend wird der Fall behandelt, in dem der Datensatz als gelöscht markiert wird:
- Der aktuelle Index ist ein Primärschlüsselindex und ist
unique_search und gibt NOT FOUND zurück. - Andernfalls springen Sie zu Schritt 9 next_rec
- Wenn der aktuelle Index ein Sekundärindex ist und der Primärschlüsselindex überprüft werden muss, suchen Sie den entsprechenden
primary record im Primärschlüsselindex und fügen Sie Rec Lock hinzu. Wenn der Primärdatensatz als gelöscht markiert ist, springt der aktuelle Sekundärindex zu Schritt 9 next_rec - Erfolg, gibt
DB_SUCCESS zurück - next_rec: Holen Sie sich den entsprechenden
prev/next record entsprechend dem Modus und springen Sie zu Schritt 3, um fortzufahren
Konzentrieren wir uns auf Schritt 3. Wenn der Datensatz Infimum oder Supremum ist, werden im Allgemeinen mehrere genera_fetches auf einer Seite ausgeführt, um den vorherigen/nächsten Datensatz abzurufen und dann den Rand der Seite zu erreichen. Für Infimum wird keine Sperre hinzugefügt und der vorherige vorherige Datensatz (d. h. das Supremum der vorherigen Seite) wird direkt aufgerufen. Für Supremum wird eine Lückensperre hinzugefügt, um die Lücke zwischen dem letzten Benutzerdatensatz der aktuellen Seite und dem ersten Benutzerdatensatz der nächsten Seite zu schützen. Zum Ablauf ist nichts weiter zu sagen: - Für die Datensätze, die die Bedingungen erfüllen, wird standardmäßig die Next-Key-Sperre hinzugefügt.
- Wenn der Sekundärindex zur Tabelle zurückgegeben wird, wird nur der Primärschlüssel erneut gesperrt.
- Für einige spezielle Szenarien werden einige
Next-key lock auf Rec lock herabgestuft (Schritt 5). - Es gibt auch einige spezielle Szenarien, in denen nur die Lückensperre hinzugefügt wird (Schritte 2 und 4).
Zusammenfassen:
Das Obige ist im Wesentlichen der Vorgang zum Hinzufügen von Transaktionssperren zu InnoDB . Durch die Kombination der Sperrvorgänge von Insert und Select werden die Prinzipien und die Implementierung von Transaktionssperren im Wesentlichen offengelegt. Dies ist das Ende dieses Artikels über die Analyse des Quellcodes der MySQL InnoDB-Transaktionssperre. Weitere relevante Inhalte zur Analyse des Quellcodes der MySQL InnoDB-Transaktionssperre finden Sie in den vorherigen Artikeln von 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird! Das könnte Sie auch interessieren:- Hauptfunktionen von MySQL Innodb: Einfügepuffer
- Detaillierte Erläuterung der Speicherverwaltung der MySQL InnoDB-Speicher-Engine
- Eine kurze Einführung in MySQL InnoDB ReplicaSet
- Detaillierte Einführung in den MySQL Innodb Index-Mechanismus
- Detaillierte Erläuterung verschiedener Sperren in der InnoDB-Speicher-Engine in MySQL
- MySQL-Speicher-Engines InnoDB und MyISAM
- So lösen Sie Phantom-Lesevorgänge in InnoDB in MySQL
|