Ein magischer MySQL-Deadlock-Troubleshooting-Datensatz

Ein magischer MySQL-Deadlock-Troubleshooting-Datensatz

Hintergrund

Apropos MySQL-Deadlock: Ich habe bereits eine grundlegende Einführung in MySQL-Sperren geschrieben. Informationen zu einigen grundlegenden MySQL-Sperren oder Deadlocks finden Sie in diesem Artikel darüber, warum Entwickler Datenbanksperren verstehen müssen. Aufgrund der oben genannten Erfahrungen dachte ich, ich könnte problemlos mit Deadlocks umgehen. Unerwarteterweise wurde an einem sonnigen Nachmittag ein weiterer Deadlock gemeldet, aber dieses Mal war es nicht so einfach, wie ich es mir vorgestellt hatte.

Probleme traten erstmals auf

Eines Nachmittags schlug das System plötzlich Alarm und warf eine Ausnahme:

Bei näherem Hinsehen stellte sich heraus, dass es sich um ein abnormales Rollback der Transaktion handelte. Es hieß, dass das Rollback aufgrund eines Deadlocks erfolgte. Es stellte sich jedoch heraus, dass es sich um ein Deadlock-Problem handelte. Da ich einige Kenntnisse über MySQL-Sperren habe, begann ich, dieses Problem aktiv zu untersuchen.

Suchen Sie zunächst in der Datenbank nach Innodb Status. Innodb Status zeichnet die Informationen zum letzten Deadlock auf. Geben Sie den folgenden Befehl ein:

ENGINE-INNODB-STATUS ANZEIGEN

Die Deadlock-Informationen lauten wie folgt und die SQL-Informationen werden einfach verarbeitet:

------------------------ 
Zuletzt erkannter Deadlock 
------------------------ 
2019-02-22 15:10:56 0x7eec2f468700 
*** (1) TRANSAKTION: 
TRANSAKTION 2660206487, AKTIV 0 Sek. Startindex lesen 
MySQL-Tabellen in Verwendung 1, gesperrt 1 
LOCK WAIT 2 Sperrstruktur(en), Heap-Größe 1136, 1 Zeilensperre(n) 
MySQL-Thread-ID 31261312, OS-Thread-Handle 139554322093824, Abfrage-ID 11624975750 10.23.134.92 erp_crm__6f73 wird aktualisiert 
/*id:3637ba36*/UPDATE tenant_config SET 
 open_card_point = 0 
 wobei tenant_id = 123 
*** (1) WARTEN AUF DIE GEWÄHRUNG DIESER SPERRE: 
Datensatzsperren, Bereichs-ID 1322, Seitennummer 534, N Bits 960, Index uidx_tenant der Tabelle „erp_crm_member_plan“. „tenant_config“ TRX-ID 2660206487, Sperrmodus X, Datensätze sperren, aber keine Lücken warten 
 *** (2) TRANSAKTION: 
TRANSAKTION 2660206486, AKTIV 0 Sek. Startindex lesen 
MySQL-Tabellen in Verwendung 1, gesperrt 1 
3 Sperrstruktur(en), Heap-Größe 1136, 2 Zeilensperren 
MySQL-Thread-ID 31261311, OS-Thread-Handle 139552870532864, Abfrage-ID 11624975758 10.23.134.92 erp_crm__6f73 wird aktualisiert 
/*id:3637ba36*/UPDATE tenant_config SET 
 open_card_point = 0 
 wobei tenant_id = 123 
*** (2) HÄLT DAS SCHLOSS: 
Datensatzsperren, Bereichs-ID 1322, Seitennummer 534, N Bits 960, Index uidx_tenant der Tabelle „erp_crm_member_plan“. „tenant_config“, TRX-ID 2660206486, Sperrmodus S 
*** (2) WARTEN AUF DIE GEWÄHRUNG DIESER SPERRE: 
Datensatzsperren, Bereichs-ID 1322, Seitennummer 534, N Bits 960, Index uidx_tenant der Tabelle „erp_crm_member_plan“. „tenant_config“ TRX-ID 2660206486, Sperrmodus X, Sperren für Datensätze, aber keine Lückenwartezeit 
 *** WIR MACHEN DIE TRANSAKTION ZURÜCK (1) 
------------

Lassen Sie mich dieses Deadlock-Protokoll kurz analysieren und erklären. Wenn Transaktion 1 die Update-Anweisung ausführt, muss sie in der Where-Bedingung die X-Sperre (Zeilensperre) für den uidx_tenant-Index erhalten. Transaktion 2 führt dieselbe Update-Anweisung aus und möchte auch die X-Sperre (Zeilensperre) für uidx_tenant erhalten. Dann tritt ein Deadlock auf und Transaktion 1 wird zurückgesetzt. Ich war damals verwirrt und erinnerte mich an die notwendigen Bedingungen für einen Deadlock:

1. Sich gegenseitig ausschließend.

2. Konditionen anfordern und einhalten.

3. Keine Entziehung der Bedingungen.

4. Warten Sie in einer Schleife.

Aus dem Protokoll können wir ersehen, dass sowohl Transaktion 1 als auch Transaktion 2 um die Zeilensperre derselben Zeile konkurrieren, was sich ein wenig vom vorherigen zyklischen Sperrwettbewerb unterscheidet. Egal, wie Sie es betrachten, die zyklische Wartebedingung kann nicht erfüllt werden. Nach der Erinnerung eines Kollegen besteht die einzige Möglichkeit darin, den Geschäftscode und das Geschäftsprotokoll zur Fehlerbehebung zu verwenden, da das Deadlock-Protokoll nicht zur Fehlerbehebung verwendet werden kann. Die Logik dieses Codes ist wie folgt:

öffentliche int saveTenantConfig(PoiContext poiContext, TenantConfigDO tenantConfig) { 
 versuchen { 
  gibt tenantConfigMapper.saveTenantConfig zurück (poiContext.getTenantId(), poiContext.getPoiId(), tenantConfig); 
 } Fang (DuplicateKeyException e) { 
  LOGGER.warn("[saveTenantConfig] Primärschlüsselkonflikt, aktualisiere den Datensatz. context:{}, config:{}", poiContext, tenantConfig); 
  gibt tenantConfigMapper.updateTenantConfig(poiContext.getTenantId(), tenantConfig) zurück; 
 } 
 }

Dieser Code bedeutet das Speichern einer Konfigurationsdatei. Wenn ein eindeutiger Indexkonflikt auftritt, wird er aktualisiert. Natürlich kann er nicht auf standardmäßige Weise geschrieben werden. Tatsächlich kann er verwendet werden

einfügen in ... 
bei doppeltem Schlüssel-Update

Derselbe Effekt kann erzielt werden, aber auch hiermit kommt es zu einem Deadlock. Mein Kollege hat mir nach dem Lesen des Codes das damalige Geschäftsprotokoll zugesendet.

Sie können sehen, dass drei Protokolle gleichzeitig aufgetreten sind, was darauf hinweist, dass ein eindeutiger Indexkonflikt aufgetreten ist, die Aktualisierungsanweisung eingegeben wurde und anschließend ein Deadlock auftrat. An diesem Punkt scheint die Antwort endlich klarer zu werden.

Betrachten wir nun unsere Tabellenstruktur wie folgt (vereinfacht):

Tabelle „tenant_config“ erstellen ( 
 `id` bigint(21) NICHT NULL AUTO_INCREMENT, 
 `tenant_id` int(11) NICHT NULL, 
 `open_card_point` int(11) DEFAULT NULL, 
 Primärschlüssel (`id`), 
 EINDEUTIGER SCHLÜSSEL `uidx_tenant` (`tenant_id`) 
) ENGINE=InnoDB STANDARD CHARSET=utf8mb4 ROW_FORMAT=KOMPAKT

Unsere tenant_id wird als eindeutiger Index verwendet und unsere Einfüge- und Aktualisierungsbedingungen basieren alle auf dem eindeutigen Index.

AKTUALISIEREN Sie tenant_config SET 
 open_card_point = 0 
 wobei tenant_id = 123

An diesem Punkt scheint es, dass das Sperren des eindeutigen Index während des Einfügens damit zusammenhängt. Als Nächstes fahren wir mit dem nächsten Schritt der eingehenden Analyse fort.

Detaillierte Analyse

Oben haben wir gesagt, dass drei Transaktionen in die Update-Anweisung eingehen. Um die Erklärung zu vereinfachen, müssen nur zwei Transaktionen gleichzeitig in die Update-Anweisung eingehen. Die folgende Tabelle zeigt unseren gesamten Prozess:

Tipp: Die S-Sperre ist eine gemeinsam genutzte Sperre und die X-Sperre ist eine Mutex-Sperre. Im Allgemeinen schließen sich X-Sperren und S,X-Sperren gegenseitig aus, S-Sperren und S-Sperren jedoch nicht gegenseitig aus.

Aus dem obigen Prozess können wir ersehen, dass der Schlüssel zu diesem Deadlock darin besteht, das S-Schloss zu erhalten. Warum müssen wir das S-Schloss erhalten, wenn wir es erneut einfügen? Weil wir einen eindeutigen Index ermitteln müssen? Wenn Sie auf der RR-Isolationsebene lesen möchten, handelt es sich um einen aktuellen Lesevorgang, sodass Sie tatsächlich eine S-Sperre hinzufügen müssen. Hier wird festgestellt, dass der eindeutige Schlüssel bereits vorhanden ist. Zu diesem Zeitpunkt wird die Ausführung der Aktualisierung durch die S-Sperren der beiden Transaktionen blockiert, wodurch der obige zirkuläre Wartezustand entsteht.

Tipp: In MVCC besteht der Unterschied zwischen dem aktuellen Lesen und dem Snapshot-Lesen darin, dass beim aktuellen Lesen jedes Mal eine Sperre erforderlich ist (gemeinsame Sperre oder Mutex-Sperre können verwendet werden), um die neuesten Daten zu erhalten, während beim Snapshot-Lesen der Snapshot zu Beginn der Transaktion gelesen wird, was durch das Rückgängigmachen des Protokolls erreicht wird.

Dies ist die Ursache für den gesamten Deadlock. Eine weitere Situation, in der dieser Deadlock auftreten kann, ist, wenn drei Einfügevorgänge gleichzeitig ausgeführt werden. Wenn die zuerst eingefügte Transaktion schließlich zurückgesetzt wird, kommt es auch bei den anderen beiden Transaktionen zu diesem Deadlock.

Lösung

Das Hauptproblem besteht darin, dass die S-Sperre entfernt werden muss. Hier sind drei Lösungen als Referenz:

  • Reduzieren Sie die RR-Isolationsstufe auf die RC-Isolationsstufe. Hier verwendet die RC-Isolationsebene das Lesen von Snapshots, sodass keine S-Sperre hinzugefügt wird.
  • Beim erneuten Einfügen select * zum Aktualisieren verwenden und eine X-Sperre hinzufügen, damit keine S-Sperre hinzugefügt wird.
  • Sie können mithilfe von Redis, ZK usw. im Voraus verteilte Sperren hinzufügen. Informationen zu verteilten Sperren finden Sie in diesem Artikel von mir. Lassen Sie uns über verteilte Sperren sprechen

Die erste Methode ist nicht sehr realistisch, schließlich lässt sich die Isolationsstufe nicht so einfach ändern. Die dritte Methode ist problematischer. Wir haben uns letztendlich für die zweite Methode entschieden.

Zusammenfassen

Nachdem ich so viel gesagt habe, möchte ich eine kurze Zusammenfassung machen. Bei der Behebung von Deadlock-Problemen reicht es manchmal nicht aus, nur das Deadlock-Protokoll zu betrachten, um das Problem zu lösen. Um das richtige Ergebnis zu erhalten, ist eine Analyse des gesamten Geschäftsprotokolls, des Codes und der Tabellenstruktur erforderlich. Natürlich gibt es oben einige grundlegende Kenntnisse über Datenbanksperren. Wenn Sie es nicht verstehen, können Sie meinen anderen Artikel Warum Entwickler über Datenbanksperren Bescheid wissen müssen lesen.

Der letzte Artikel wurde in JGrowing-CaseStudy aufgenommen, einem umfassenden, hervorragenden Java-Lernpfad, der von der Community erstellt wurde. Wenn Sie an der Wartung von Open-Source-Projekten teilnehmen möchten, können Sie diese gemeinsam erstellen. Die GitHub-Adresse lautet: https://github.com/javagrowing/JGrowing

Das ist alles für diesen Artikel. Ich hoffe, dass der Inhalt dieses Artikels für Ihr Studium oder Ihre Arbeit von gewissem Referenzwert ist. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM.

Das könnte Sie auch interessieren:
  • Ausführliche Erläuterung der Mysql-Deadlock-Anzeige und Deadlock-Entfernung
  • Die normale Methode der MySQL-Deadlock-Prüfungsverarbeitung
  • Ursachen und Lösungen für MySQL-Deadlocks
  • Detaillierte Analyse von MySQL-Deadlock-Problemen
  • MySQL-Deadlock-Routine: inkonsistente Batch-Einfügereihenfolge unter eindeutigem Index
  • Analyse eines MySQL-Deadlock-Szenariobeispiels
  • Analyse des Purge-Deadlock-Problems in der MySQL-Datenbank
  • Detaillierte Erläuterung der verteilten Deadlock-Erkennung und -Beseitigung durch SQL

<<:  Zusammenfassung verschiedener Methoden für Vue zum Erreichen dynamischer Stile

>>:  Tutorial zur Installation von Android Studio unter Ubuntu 19 und darunter

Artikel empfehlen

Implementierung der DOM-Operation in React

Inhaltsverzeichnis Vorherige Wörter Anwendungssze...

Schreiben Sie mit CSS in drei Schritten einen Coupon für eine Shopping-Card

Heute ist der 618. und alle großen Einkaufszentre...

Teilen Sie 8 sehr nützliche CSS-Entwicklungstools

CSS3-Mustergalerie Diese CSS3-Musterbibliothek ze...

Mehrere häufig verwendete Single-Page-Anwendungswebsite-Sharing

CSS3Bitte Schauen Sie sich diese Website selbst a...

Details zur Funktionsverschachtelung und zu Funktionsabschlüssen in js

Inhaltsverzeichnis 1. Geltungsbereich 2. Funktion...

HTML+Sass implementiert HambergurMenu (Hamburger-Menü)

Vor ein paar Tagen habe ich mir ein Video von ein...