Detaillierte Erläuterung des MySQL Multi-Version Concurrency Control Mechanism (MVCC)-Quellcodes

Detaillierte Erläuterung des MySQL Multi-Version Concurrency Control Mechanism (MVCC)-Quellcodes

1. Einleitung

Als Datenbank-Enthusiast habe ich selbst einen einfachen SQL-Parser und eine Speicher-Engine geschrieben, aber ich bin damit immer noch nicht zufrieden. <<Transaktionsverarbeitung – Konzepte und Technologien>> ist zwar sehr ausführlich, kann Ihnen jedoch nur einen allgemeinen Überblick verschaffen und ist nicht bei der Arbeit mit einer echten Datenbank hilfreich. Dank cmake kann ich mit xcode MySQL auf dem Mac debuggen, sodass ich die verschiedenen Implementierungsdetails würdigen kann.

(Hinweis: Die in diesem Artikel verwendete MySQL-Version ist MySQL-5.6.35)

2. MVCC (Multi-Version Concurrency Control Mechanism)

Isolation kann auch als Parallelitätskontrolle, Serialisierbarkeit usw. bezeichnet werden. Wenn man über Parallelitätskontrolle spricht, denkt man zuerst an Sperren. MySQL implementiert die Serialisierbarkeit von Aktualisierungen mithilfe von zweiphasigen Sperren. Gleichzeitig wird zur Beschleunigung der Abfrageleistung der MVCC-Mechanismus (Multi Version Concurrency Control) übernommen, sodass konsistente Versionen ohne Sperren erhalten werden können.

2.1 Wiederholbares Lesen

MySQL implementiert Repeatable Read durch MVCC und Next-Key Lock. Die Idee von MVCC besteht darin, die Versionsänderungen von Daten aufzuzeichnen und Benutzern durch geschickte Auswahl verschiedener Datenversionen konsistente Ergebnisse zu präsentieren. Wie in der folgenden Abbildung dargestellt:

In der obigen Abbildung ist die Anfangsversion von (A=50|B=50) 1.

1. Wenn Transaktion t1 A auswählt, sieht sie Version 1, d. h. A = 50

2. Transaktion t2 ändert A und B und aktualisiert die Version auf 2, d. h. A = 0, B = 100

3. Wenn Transaktion t1 B erneut auswählt, ist die angezeigte Version immer noch 1, d. h. B = 50

Dadurch wird der Einfluss der Version isoliert und A+B ergibt immer 100.

2.2 Commit lesen

Wenn das zuletzt festgeschriebene Ergebnis ohne Versionskontrollmechanismus gelesen wird, lautet die Isolationsebene „Read Commit“, wie in der folgenden Abbildung dargestellt:

In diesem Fall müssen Sie einen Sperrmechanismus (z. B. „Auswählen zum Aktualisieren“) verwenden, um die Datensätze A und B zu sperren und so korrekte und konsistente Ergebnisse zu erhalten, wie in der folgenden Abbildung dargestellt:

2.3 Vorteile von MVCC

Wenn wir schreibgeschützte Vorgänge für bestimmte Daten ausführen müssen, um deren Konsistenz zu prüfen, z. B. um zu überprüfen, ob die Konten ausgerichtet sind, möchten wir keine Sperren hinzufügen, die zu großen Leistungseinbußen führen. Derzeit hat die konsistente Version von MVCC einen großen Vorteil.

3. MVCC (Implementierungsmechanismus)

In diesem Abschnitt wird zunächst der Implementierungsmechanismus von MVCC erläutert. Beachten Sie, dass MVCC nur für reine Auswahl gültig ist (ausgenommen Sperrvorgänge wie Auswahl zum Aktualisieren, Sperren im Freigabemodus und Aktualisieren\Einfügen usw.).

3.1. Wählen Sie den laufenden Stapel aus

Lassen Sie uns zunächst den laufenden Prozess einer allgemeinen SQL-Abfrage im MySQL-Quellcode verfolgen. Die SQL lautet (select * from test);

Der laufende Stapel ist:

handle_one_connection Das Netzwerkmodell von MySQL besteht aus einer Anfrage und einem Thread

|-eine_Verbindung_verwalten

|-Befehl ausführen

|-Versandbefehl

|-mysql_parse SQL analysieren

|-mysql_execute_command

|-execute_sqlcom_select Ausführen der Select-Anweisung

|-Auswahl des Handles

... eine Reihe von Parse-Join- und anderen Operationen, die mich im Moment nicht interessieren

|-*tab->read_record.read_record Datensatz lesen

Da die Standardisolationsstufe von MySQL repeatable_read (RR) ist, ist read_record überladen als
rr_sequential (Derzeit beschäftigen wir uns nicht mit dem Prozess der Zeilenauswahl durch Scannen des Index und anschließendes Filtern nach Bedingung.) Weiter verfolgen:

Datensatz lesen

|-rr_sequential

|-ha_rnd_next

|-ha_innobase::rnd_next Dies ist die InnoDB-Engine.

|-general_fetch

|-Zeilensuche_nach_MySQL

|-lock_clust_rec_cons_read_sees Hier wird die Version ermittelt und ausgewählt

Werfen wir einen Blick in diese Funktion:

bool lock_clust_rec_cons_read_sees(const rec_t* rec /*eine von innodb gescannte Zeile*/,...){
	...
	// Holen Sie sich die zuletzt geänderte Version trx_id (Transaktions-ID) aus der aktuell gescannten Zeile
	trx_id = row_get_rec_trx_id(rec, index, offsets);
	// Bestimmen Sie den Zeilen-Snapshot, der durch die Parameter angezeigt wird (konsistente Snapshot-Ansicht und Transaktions-ID) return(read_view_sees_trx_id(view, trx_id));
}

3.2. Erstellungsprozess von read_view

Konzentrieren wir uns zunächst auf den Erstellungsprozess der Konsistenzansicht. Schauen wir uns zunächst die read_view-Struktur an:

Struktur read_view_t{
	// Da die Reihenfolge umgekehrt ist, sind „low“ und „up“ vertauscht. // Sie können die Hochwassermarke der aktuellen Zeilenversion sehen, und >= low_limit_id ist nicht sichtbar. trx_id_t low_limit_id;
	// Sie können die Niedrigwassermarke der aktuellen Zeilenversion sehen, <up_limit_id ist zu sehen trx_id_t up_limit_id;
	// Die Anzahl der aktuell aktiven Transaktionen (d. h. nicht festgeschriebene Transaktionen) ulint n_trx_ids;
	// Holen Sie sich das Array der derzeit aktiven Transaktions-IDs in umgekehrter Reihenfolge // Seine up_limit_id<tx_id<low_limit_id
	trx_id_t* trx_ids;	
	//Erstellen Sie die Transaktions-ID der aktuellen Ansicht
	trx_id_t Ersteller_trx_id;
	//Konsistenzansichtsliste im Transaktionssystem UT_LIST_NODE_T(read_view_t) view_list;
};

Durch das Debuggen wird dann festgestellt, dass die Erstellung der read_view-Struktur auch im obigen rr_sequential ausgeführt wird, und der Aufrufstapel wird fortgesetzt:

rr_sequenziell

|-ha_rnd_next

|-rnd_nächste

|-index_first Gehe zum aktuellen Zweig index_first, wenn start_of_scan true ist

|-index_lesen

|-Zeilensuche_nach_MySQL

|-trx_assign_read_view

Schauen wir uns einen Zweig in row_search_for_mysql an:

Zeilensuche nach MySQL:
// Die Konsistenzansicht wird nur erstellt, wenn der sperrenfreie Modus ausgewählt ist, sonst wenn (prebuilt->select_lock_type == LOCK_NONE) { // Eine Konsistenzansicht erstellen trx_assign_read_view(trx);
		vorgefertigt->sql_stat_start = FALSE;
}

Der obige Kommentar ist der Grund, warum die Auswahl zum Aktualisieren (im Freigabemodell) nicht MVCC folgt. Lassen Sie uns die Funktion trx_assign_read_view weiter analysieren:

trx_assign_lesen_anzeigen

|-lesen_anzeigen_jetzt_öffnen

|-read_view_open_now_low

Nun sind wir endlich beim Hauptstadium der Erstellung von read_view angelangt. Der Hauptprozess ist in der folgenden Abbildung dargestellt:

Der Code-Prozess ist:

statisches Lese-Ansicht_t* Lese-Ansicht_jetzt_öffnen_niedrig(trx_id_t cr_trx_id,mem_heap_t* Heap)
{
	read_view_t* Ansicht;
	// Im aktuellen Transaktionssystem wird max_trx_id (d. h. trx_id, die nicht zugewiesen wurde) auf low_limit_no gesetzt
	Ansicht -> Untergrenze_Nr. = trx_sys -> max_trx_id;
	Ansicht->Untergrenzen-ID = Ansicht->Untergrenzen-Nr.
	// Der CreateView-Konstruktor entfernt nicht aktuelle Transaktionen und Transaktionen, die im Speicher festgeschrieben wurden, d. h. die Beurteilungsbedingung ist // trx->id != m_view->creator_trx_id&& !trx_state_eq(trx, TRX_STATE_COMMITTED_IN_MEMORY) // und fügt sie dann der aktuellen Ansichtsliste hinzu ut_list_map(trx_sys->rw_trx_list, &trx_t::trx_list, CreateView(view));
	wenn (Ansicht->n_trx_ids > 0) {
		//Setzen Sie die Mindest-ID im aktuellen Transaktionssystem auf up_limit_id, da diese in umgekehrter Reihenfolge vorliegt: view->up_limit_id = view->trx_ids[view->n_trx_ids - 1];
	} anders {
		// Wenn außer der aktuellen Transaktion keine aktive Transaktion vorhanden ist, setzen Sie sie auf low_limit_id
		Ansicht->Up_Limit_ID = Ansicht->Lokales_Limit_ID;
	}
	// Ignoriere die Löschtransaktion. Beim Löschen ist die aktuelle Transaktions-ID 0
	wenn (cr_trx_id > 0) {
		lesen_ansicht_hinzufügen(ansicht);
	}
	//Gibt die konsistente Ansicht zurück return(view);
}

3.3. Sichtbarkeit der Zeilenversion

Aus den obigen lock_clust_rec_cons_read_sees können wir erkennen, dass die Sichtbarkeit der Zeilenversion durch die Funktion read_view_sees_trx_id bestimmt wird:

/****************************************************************************//**
Überprüft, ob eine Lese-Ansicht die angegebene Transaktion sieht.
@return true wenn sieht */
UNIV_INLINE
bool
lesen_anzeigen_sieht_trx_id(
/*====================*/
	const read_view_t* Ansicht, /*!< in: Ansicht lesen */
	trx_id_t trx_id) /*!< in: trx-ID */
{
	wenn (trx_id < Ansicht->up_limit_id) {

		Rückkehr (wahr);
	} sonst wenn (trx_id >= view->low_limit_id) {

		Rückkehr (falsch);
	} anders {
		ulint niedriger = 0;
		ulint oben = Ansicht->n_trx_ids - 1;

		ut_a(Ansicht->n_trx_ids > 0);

		Tun {
			ulint mid = (unterer + oberer) >> 1;
			trx_id_t mid_id = Ansicht->trx_ids[mid];

			wenn (mid_id == trx_id) {
				Rückkehr(FALSCH);
			} sonst wenn (mid_id < trx_id) {
				wenn (Mitte > 0) {
					oben = Mitte - 1;
				} anders {
					brechen;
				}
			} anders {
				unten = Mitte + 1;
			}
		} während (unterer <= oberer);
	}

	Rückkehr (wahr);
}

Tatsächlich handelt es sich bei der obigen Funktion um eine binäre Suche. read_view speichert tatsächlich alle Transaktions-IDs der aktuell aktiven Transaktion. Wenn die Transaktions-ID, die der Änderung der aktuellen Zeilenversion entspricht, nicht in der aktuell aktiven Transaktion enthalten ist, wird true zurückgegeben, was bedeutet, dass die aktuelle Version sichtbar ist, andernfalls ist sie unsichtbar, wie in der folgenden Abbildung dargestellt.

Nach der Rückgabe von lock_clust_rec_cons_read_sees oben:

wenn (UNIV_LIKELY(srv_force_recovery < 5)
			    && !lock_clust_rec_cons_read_sees(
				    aufzeichnen, indexieren, offsets, trx->read_view)){
	// Derzeit wird die Situation bearbeitet, in der die aktuelle Version nicht sichtbar ist. // Verwenden Sie Undolog, um zu einer konsistenten sichtbaren Version zurückzukehren. err = row_sel_build_prev_vers_for_mysql(
					trx->Ansicht lesen, Clusterindex,
					vorgefertigt, rec, &offsets, &heap,
					&old_vers, &mtr);			    
} anders{
	// Sichtbar, dann zurück }

3.4. Undolog-Suchvorgang für sichtbare Versionen

Lassen Sie uns nun die Funktion row_sel_build_prev_vers_for_mysql untersuchen:

row_sel_build_prev_vers_for_mysql

|-row_vers_build_for_consistent_read

Hauptsächlich wird die Methode row_ver_build_for_consistent_read aufgerufen, um die sichtbare Version zurückzugeben:

dberr_t row_vers_build_for_consistent_read(...)
{
	......
	für(;;){
		err = trx_undo_prev_version_build(rec, mtr,version,index,*offsets, heap,&prev_version);
		......
		trx_id = row_get_rec_trx_id(vorherige_Version, Index, *Offsets);
		// Wenn die aktuelle Zeilenversion der konsistenten Ansicht entspricht, returniere if (read_view_sees_trx_id(view, trx_id)) {
			......
			brechen;
		}
		// Wenn die aktuelle Zeilenversion nicht übereinstimmt, gehe weiter zurück zur vorherigen Version (kehre zur For-Schleife zurück)
		Version = vorherige Version;
	}
	......
}

Der gesamte Vorgang ist in der folgenden Abbildung dargestellt:

Die Wiederherstellung der entsprechenden Version von Zeilendatensätzen durch Undolog ist ein komplizierter Vorgang. Aus Platzgründen wird er hier weggelassen.

3.5. Besprechen Sie, wann read_view erstellt werden soll

Im Code von row_search_for_mysql, der eine konsistente Ansicht erstellt

// Wählt nur im Nicht-Sperrmodus aus, erstellt konsistente Ansichten, sonst wenn (prebuilt->select_lock_type == LOCK_NONE) { // Konsistente Ansichten erstellen trx_assign_read_view(trx);
		vorgefertigt->sql_stat_start = FALSE;
}

Es gibt einen solchen Code in trx_assign_read_view

// Eine konsistente Ansicht wird nur einmal pro Transaktion erstellt, wenn (!trx->read_view) {
		trx->read_view = read_view_jetzt_öffnen(
			trx->id, trx->global_read_view_heap);
		trx->global_read_view = trx->read_view;
	}

Durch die Kombination dieser beiden Codeteile wird in einer Transaktion daher nur dann eine konsistente Ansicht erstellt, wenn die Auswahl zum ersten Mal ausgeführt wird (ohne Sperren), wie in der folgenden Abbildung dargestellt:

Der Autor hat ein solches Szenario konstruiert und simuliert und es ist tatsächlich wahr.

4. Einige Phänomene, die durch die gleichzeitige Wirkung von MVCC und Sperren verursacht werden

MySQL verwendet MVCC und Zweiphasensperre (2PL), um Leistung und Konsistenz auszugleichen. Da MySQL jedoch nur bei der Auswahl eine konsistente Ansicht erstellt und dies bei Sperrvorgängen wie der Aktualisierung nicht tut, treten einige seltsame Phänomene auf. Wie in der folgenden Abbildung dargestellt:

Wenn man versteht, dass update nicht der konsistenten Ansicht (read_view) folgt, select jedoch der konsistenten Ansicht (read_view) folgt, lässt sich dieses Phänomen gut erklären.
Wie in der folgenden Abbildung dargestellt:

V. Fazit

MySQL verwendet viele komplexe Mechanismen zum Leistungsausgleich und ACID, 2PL (Two-Phase Locking) und MVCC sind typische Beispiele für seine Implementierung. Glücklicherweise ist das Debuggen über IDEs wie Xcode praktisch, sodass die Implementierung verschiedener Mechanismen sehr genau und bequem verfolgt werden kann. Ich hoffe, dass dieser Artikel Lesern helfen kann, die gerne MySQL-Quellcode studieren.

Oben finden Sie eine ausführliche Erläuterung des Quellcodes des MySQL-Mehrversions-Parallelitätskontrollmechanismus (MVCC). Weitere Informationen zum MySQL-Parallelitätskontrollmechanismus MVCC finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Implementierung der MySQL-Mehrversions-Parallelitätskontrolle MVCC
  • Implementierung der MVCC-Mehrversions-Parallelitätskontrolle von MySQL
  • Detaillierte Untersuchung der MySQL-Mehrversions-Parallelitätskontrolle MVCC
  • Analyse des zugrunde liegenden Prinzips der MySQL-Mehrversions-Parallelitätskontrolle MVCC
  • Implementierung von MySQL Multi-version Concurrency Control MVCC
  • Details zur Mysql MVCC-Mehrversions-Parallelitätssteuerung

<<:  Zusammenfassung zur Positionierung in CSS

>>:  Detaillierte Erklärung des Ref-Attributs von Vue

Artikel empfehlen

Umfassende Zusammenfassung zu MySQL GTID

Inhaltsverzeichnis 01 Einführung in GTID 02 Wie G...

Detaillierte Erläuterung der MySQL-Remoteverbindungsberechtigung

1. Melden Sie sich bei der MySQL-Datenbank an mys...

10 zu wenig genutzte oder missverstandene HTML-Tags

Hier sind 10 HTML-Tags, die zu wenig verwendet od...

So fügen Sie Emoji-Ausdrücke in MySQL ein

Vorwort Als ich heute ein Feedback-Formular für e...

MySQL-Interviewfragen: So richten Sie Hash-Indizes ein

Zusätzlich zu den B-Tree-Indizes bietet MySQL auc...

Sollte ich JQuery aufgeben?

Inhaltsverzeichnis Vorwort Was soll verwendet wer...

Tutorial zu den Grundlagen von JavaScript und JQuery Framework

Inhaltsverzeichnis 1. JS-Objekt DOM –1, Funktion ...

Beispieltest MySQL-Enumerationstyp

Bei der Entwicklung eines Projekts stößt man häuf...