Detaillierte Erklärung langer Transaktionsbeispiele in MySQL

Detaillierte Erklärung langer Transaktionsbeispiele in MySQL

Vorwort:

Die Artikelserie „Erste Schritte mit MySQL“ ist beendet. In Zukunft werde ich mich in meinen Artikeln weiterhin auf MySQL konzentrieren und hauptsächlich die Szenarien beschreiben, die ich bei meiner jüngsten Arbeit und meinem Studium erlebt habe, oder meine eigenen Gefühle und Ideen. Die nachfolgenden Artikel sind möglicherweise nicht so kohärent, aber ich hoffe trotzdem auf Ihre Unterstützung. Kommen wir zurück zum Thema. Dieser Artikel stellt hauptsächlich den Inhalt im Zusammenhang mit langen MySQL-Transaktionen vor. Was passiert beispielsweise, wenn wir eine Transaktion starten, diese aber nicht festgeschrieben oder zurückgesetzt wurde? Wie sollten wir mit der Wartesituation bei Transaktionen umgehen? Dieser Artikel gibt Ihnen die Antwort.

Hinweis: In diesem Artikel geht es nicht um die Erörterung von Transaktionsisolationsebenen und verwandten Funktionen. Stattdessen werden die mit Langzeittransaktionen verbundenen Gefahren sowie deren Überwachung und Handhabung vorgestellt. Dieser Artikel basiert auf Experimenten, die mit MySQL Version 5.7.23 und der Isolationsebene „Non-Repeatable Read“ (RR) durchgeführt wurden.

1. Was ist eine Long-Transaktion?

Zunächst müssen wir wissen, was eine lange Transaktion ist. Wie der Name schon sagt, handelt es sich um eine Transaktion, die lange ausgeführt wird und für lange Zeit nicht festgeschrieben wird. Sie kann auch als große Transaktion bezeichnet werden. Diese Art von Transaktion verursacht häufig viele Blockierungen und Sperrzeitüberschreitungen, was leicht zu Master-Slave-Verzögerungen führen kann. Versuchen Sie, lange Transaktionen zu vermeiden.

Im Folgenden werde ich zeigen, wie man eine Transaktion startet und eine lange Transaktion simuliert:

#Angenommen, wir haben eine stu_tb-Tabelle mit der folgenden Struktur und den folgenden Daten mysql> show create table stu_tb\G
*************************** 1. Reihe ***************************
  Tabelle: stu_tb
Tabelle erstellen: CREATE TABLE `stu_tb` (
 `increment_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Primärschlüssel automatisch inkrementieren',
 `stu_id` int(11) NOT NULL KOMMENTAR 'Studenten-ID',
 `stu_name` varchar(20) DEFAULT NULL COMMENT 'Studentenname',
 `create_time` Zeitstempel NICHT NULL STANDARD CURRENT_TIMESTAMP KOMMENTAR 'Erstellungszeit',
 `update_time` Zeitstempel NICHT NULL STANDARD CURRENT_TIMESTAMP BEI UPDATE CURRENT_TIMESTAMP KOMMENTAR 'Änderungszeit',
 PRIMÄRSCHLÜSSEL (`increment_id`),
 EINZIGARTIGER SCHLÜSSEL `uk_stu_id` (`stu_id`) MIT BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8 COMMENT='Test-Studententabelle'
1 Zeile im Satz (0,01 Sek.)

mysql> wähle * aus stu_tb;
+--------------+--------+----------+---------------------+---------------------+
| Inkrement-ID | Stu-ID | Stu-Name | Erstellungszeit | Aktualisierungszeit |
+--------------+--------+----------+---------------------+---------------------+
| 1 | 1001 | von1 | 15.09.2019 14:27:34 | 15.09.2019 14:27:34 |
| 2 | 1002 | dfsfd | 15.09.2019 14:27:34 | 15.09.2019 14:27:34 |
| 3 | 1003 | fdgfg | 15.09.2019 14:27:34 | 15.09.2019 14:27:34 |
| 4 | 1004 | sdfsdf | 15.09.2019 14:27:34 | 15.09.2019 14:27:34 |
| 5 | 1005 | dsfsdg | 15.09.2019 14:27:34 | 15.09.2019 14:27:34 |
| 6 | 1006 | fgd | 15.09.2019 14:27:34 | 15.09.2019 14:27:34 |
| 7 | 1007 | fgds | 15.09.2019 14:27:34 | 15.09.2019 14:27:34 |
| 8 | 1008 | dgfsa | 15.09.2019 14:27:34 | 15.09.2019 14:27:34 |
+--------------+--------+----------+---------------------+---------------------+
8 Zeilen im Satz (0,00 Sek.)

#Öffnen Sie eine Transaktion explizit, verwenden Sie begin oder start transaction
mysql> Transaktion starten;
Abfrage OK, 0 Zeilen betroffen (0,00 Sek.)

mysql> select * from stu_tb where stu_id = 1006 für Update;
+--------------+--------+----------+---------------------+---------------------+
| Inkrement-ID | Stu-ID | Stu-Name | Erstellungszeit | Aktualisierungszeit |
+--------------+--------+----------+---------------------+---------------------+
| 6 | 1006 | fgd | 15.09.2019 14:27:34 | 15.09.2019 14:27:34 |
+--------------+--------+----------+---------------------+---------------------+
1 Zeile im Satz (0,01 Sek.)

#Wenn wir die vorherige Transaktion nicht rechtzeitig festschreiben, wird diese Transaktion zu einer langen Transaktion. Wenn andere Sitzungen mit diesen Daten arbeiten möchten, müssen sie weiter warten.

2. So finden Sie Long-Transaktionen

Wenn wir auf ein Problem beim Warten auf eine Transaktion stoßen, müssen wir zunächst die ausgeführte Transaktion finden. Die Tabelle information_schema.INNODB_TRX enthält die Transaktionsinformationen, die derzeit in InnoDB ausgeführt werden. Diese Tabelle gibt die Startzeit der Transaktion an. Mit einer kleinen Berechnung können wir die Ausführungszeit der Transaktion ermitteln.

mysql> wähle t.*,to_seconds(jetzt())-to_seconds(t.trx_started) Leerlaufzeit aus INFORMATION_SCHEMA.INNODB_TRX t \G
*************************** 1. Reihe ***************************
     trx_id: 6168
     trx_state: LÄUFT
    trx_started: 16.09.2019 11:08:27
  trx_requested_lock_id: NULL
   trx_wait_started: NULL
    trx_gewicht: 3
  trx_mysql_thread_id: 11
     trx_query: NULL
  trx_operation_state: NULL
   trx_tables_in_use: 0
   trx_tables_locked: 1
   trx_lock_structs: 3
  trx_lock_memory_bytes: 1136
   trx_rows_locked: 2
   trx_rows_modified: 0
 trx_concurrency_tickets: 0
  trx_isolation_level: WIEDERHOLBARES LESEN
   trx_unique_checks: 1
 trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
 trx_adaptive_hash_latched: 0
 trx_adaptive_hash_timeout: 0
   trx_ist_schreibgeschützt: 0
trx_autocommit_non_locking: 0
     Leerlaufzeit: 170

Im Ergebnis wird die Leerlaufzeit berechnet. Sie stellt die Dauer der Transaktion dar. Die trx_query der Transaktion ist jedoch NUL. Das bedeutet nicht, dass die Transaktion nichts ausgeführt hat. Eine Transaktion kann mehrere SQL-Anweisungen enthalten. Wenn die SQL-Anweisung ausgeführt wird, wird sie nicht mehr angezeigt. Die aktuelle Transaktion wird ausgeführt und InnoDB weiß nicht, ob es für diese Transaktion nachfolgende SQL-Anweisungen geben wird oder wann sie festgeschrieben wird. Daher kann trx_query keine sinnvollen Informationen liefern.

Was ist, wenn wir das von dieser Transaktion ausgeführte SQL sehen möchten, um zu sehen, ob wir die lange Transaktion beenden können? Wir können es in Verbindung mit anderen Systemtabellen abfragen. Die spezifische SQL-Abfrage lautet wie folgt:

mysql> wähle now(),(UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(a.trx_started)) diff_sec,b.id,b.user,b.host,b.db,d.SQL_TEXT aus information_schema.innodb_trx ein innerer Join
  -> information_schema.PROCESSLIST b
  -> auf a.TRX_MYSQL_THREAD_ID=b.id und b.command = 'Sleep'
  -> innerer Join performance_schema.threads c ON b.id = c.PROCESSLIST_ID
  -> innerer Join performance_schema.events_statements_current d ON d.THREAD_ID = c.THREAD_ID;
+---------------------+----------+----+------+-----------+--------+--------+-----------------------------------------------------------+
| jetzt() | diff_sec | ID | Benutzer | Host | db | SQL_TEXT |
+---------------------+----------+----+------+-----------+--------+--------+-----------------------------------------------------------+
| 16.09.2019 14:06:26 | 54 | 17 | root | localhost | testdb | select * from stu_tb where stu_id = 1006 für Update |
+---------------------+----------+----+------+-----------+--------+--------+-----------------------------------------------------------+

In den obigen Ergebnissen hat diff_sec dieselbe Bedeutung wie idle_time oben, also die Anzahl der Sekunden, die diese Transaktion dauert. SQL_TEXT gibt das gerade von der Transaktion ausgeführte SQL an. Die obige Anweisung kann jedoch nur das zuletzt in der Transaktion ausgeführte SQL finden. Wir wissen, dass eine Transaktion mehrere SQLs enthalten kann. Dann möchten wir abfragen, welche SQLs von dieser nicht festgeschriebenen Transaktion ausgeführt wurden. Kann diese Abfrage erfüllt werden? Die Antwort ist, dass sie auch durch Kombinieren der Systemtabelle events_statements_history erfüllt werden kann. Die folgende Anweisung fragt den gesamten von der Transaktion ausgeführten SQL-Befehl ab:

mysql> wähle now(),(UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(a.trx_started)) diff_sec,b.id,b.user,b.host,b.db,d.SQL_TEXT aus information_schema.innodb_trx ein innerer Join
  -> information_schema.PROCESSLIST b
  -> auf a.TRX_MYSQL_THREAD_ID=b.id und b.command = 'Sleep'
  -> innerer Join performance_schema.threads c ON b.id = c.PROCESSLIST_ID
  -> innerer Join performance_schema.events_statements_current d ON d.THREAD_ID = c.THREAD_ID;
+---------------------+----------+----+------+-----------+--------+--------+-----------------------------------------------------------+
| jetzt() | diff_sec | ID | Benutzer | Host | db | SQL_TEXT |
+---------------------+----------+----+------+-----------+--------+--------+-----------------------------------------------------------+
| 16.09.2019 14:06:26 | 54 | 17 | root | localhost | testdb | select * from stu_tb where stu_id = 1006 für Update |
+---------------------+----------+----+------+-----------+--------+--------+-----------------------------------------------------------+

Aus den obigen Ergebnissen können wir alle von der Transaktion ausgeführten SQL-Anweisungen von Anfang bis jetzt sehen. Nachdem wir alle relevanten Informationen der Transaktion abgefragt haben, können wir feststellen, ob die Transaktion abgebrochen werden kann, um zu vermeiden, dass andere Transaktionen beeinträchtigt werden und Wartezeiten entstehen.

Lassen Sie mich das etwas näher ausführen. Lange Transaktionen führen sehr wahrscheinlich zu Blockierungen oder Deadlocks. Normalerweise können wir zuerst die Ansicht sys.innodb_lock_waits abfragen, um festzustellen, ob es eine Transaktionsblockierung gibt:

#Nehmen wir an, eine Transaktion führt select * from stu_tb where stu_id = 1006 für das Update aus
#Eine andere Transaktion führt das Update stu_tb aus und setzt stu_name = 'wang', wobei stu_id = 1006

mysql> wähle * aus sys.innodb_lock_waits\G
*************************** 1. Reihe ***************************
        wait_started: 16.09.2019 14:34:32
          Wartezeit: 00:00:03
        Wartezeit_Sekunden: 3
        gesperrte_Tabelle: `testdb`.`stu_tb`
        locked_index: uk_stu_id
         gesperrter Typ: AUFZEICHNUNG
       wartende_trx_id: 6178
     waiting_trx_started: 16.09.2019 14:34:32
       Wartezeit: 00:00:03
   waiting_trx_rows_locked: 1
  waiting_trx_rows_modified: 0
         warte_pid: 19
        waiting_query: Aktualisiere stu_tb, setze stu_name = 'wang', wobei stu_id = 1006
       Wartesperren-ID: 6178:47:4:7
      Wartesperrmodus: X
       Blockierungs-TRX-ID: 6177
        Blockierungs-PID: 20
       Blockierungsabfrage: NULL
      Sperrsperr-ID: 6177:47:4:7
     Blockierungssperrmodus: X
    blocking_trx_started: 16.09.2019 14:18:44
      Blockierung_TRX_Alter: 00:15:51
  Blockierung_TRX_Zeilen_gesperrt: 2
 Blockieren von TRX-Zeilen geändert: 0
   sql_kill_blocking_query: KILL-ABFRAGE 20
sql_kill_blocking_connection: KILL 20

Die obigen Ergebnisse zeigen das blockierte SQL und den Sperrtyp. Noch leistungsfähiger ist, dass auch die Anweisung zum Beenden der Sitzung angegeben wird. Es wurde jedoch kein SQL gefunden, das die Sitzungsausführung blockiert. Wenn wir genauere Informationen erhalten möchten, können wir die folgende Anweisung verwenden:

mysql> AUSWÄHLEN
  -> temporär*,
  -> c.SQL_Text blockierender_SQL_Text,
  -> p.HOST blockierender_Host
  -> VON
  -> (
  -> AUSWÄHLEN
  -> r.trx_state beobachtet_trx_state,
  -> r.trx_id warte_trx_id,
  -> r.trx_mysql_thread_Id Wartethread,
  -> r.trx_query warte_abfrage,
  -> b.trx_state Blockierungs-TRX_State,
  -> b.trx_id blockierende_trx_id,
  -> b.trx_mysql_thread_id blockierender Thread,
  -> b.trx_query blockierende_Abfrage
  -> VON
  -> information_schema.innodb_lock_waits w
  -> INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
  -> INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id
  -> ) tmp,
  -> information_schema.PROCESSLIST p,
  -> PERFORMANCE_SCHEMA.events_statements_current c,
  -> PERFORMANCE_SCHEMA.threads t
  -> WO
  -> tmp.blocking_thread = p.id
  -> UND t.thread_id = c.THREAD_ID
  -> UND t.PROCESSLIST_ID = p.id \G
*************************** 1. Reihe ***************************
 watching_trx_state: SPERRE WARTEN
  wartende_trx_id: 6180
  Wartethread: 19
   waiting_query: Aktualisiere stu_tb, setze stu_name = 'wang', wobei stu_id = 1006
blocking_trx_state: LÄUFT
  Blockierungs-TRX-ID: 6177
  blockierender_Thread: 20
  Blockierungsabfrage: NULL
 blocking_sql_text: wähle * aus stu_tb, wobei stu_id = 1006 für Update
   blockierender_Host: lokaler Host

Die obigen Ergebnisse sind klarer. Wir können die von den blockierenden und blockierten Transaktionen ausgeführten Anweisungen deutlich sehen, was uns bei der Fehlerbehebung hilft und uns hilft zu bestätigen, ob die blockierte Sitzung beendet werden kann.

3. Überwachen Sie lange Transaktionen

In der Praxis müssen wir lange Transaktionen überwachen und einen Schwellenwert festlegen, beispielsweise 30 Sekunden. Transaktionen, deren Ausführung länger als 30 Sekunden dauert, sind lange Transaktionen und müssen aufgezeichnet und gemeldet werden, um das Managementpersonal an die Bearbeitung zu erinnern. Das Überwachungsskript ist unten aufgeführt. Sie können es zu Rate ziehen und nach Bedarf ändern:

#!/bin/bash
# -----------------------------------------------------------------------------------
# Dateiname: long_trx.sh
# Beschreiben Sie: Überwachung langer Transaktionen
# Revision: 1.0
# Datum: 16.09.2019
# Autor: wang

/usr/local/mysql/bin/mysql -N -uroot -pxxxxxx -e "select now(),(UNIX_TIMESTAMP(now()) - UNIX_TIMESTAMP(a.trx_started)) diff_sec,b.id,b.user,b.host,b.db,d.SQL_TEXT aus information_schema.innodb_trx ein innerer Join
information_schema.PROCESSLIST b
bei a.TRX_MYSQL_THREAD_ID=b.id und b.command = 'Sleep'
Innerer Join performance_schema.threads c ON b.id = c.PROCESSLIST_ID
innerer Join performance_schema.events_statements_current d ON d.THREAD_ID = c.THREAD_ID;" | während des Lesens ABCDEFGH
Tun
 wenn [ "$C" -gt 30 ]
   Dann
   echo $(Datum +"%J-%m-%d %H:%M:%S")
   echo "Prozess-ID[$D] $E@$F in db[$G] halte Transaktionszeit $C SQL:$H"
 fi
fertig >> /tmp/longtransaction.txt

Zur kurzen Erklärung: -gt 30 bedeutet hier 30 Sekunden. Wenn es 30 Sekunden überschreitet, wird es als lange Transaktion betrachtet. Sie können es entsprechend den tatsächlichen Anforderungen anpassen. Fügen Sie das Skript der geplanten Aufgabe hinzu und es wird ausgeführt.

Zusammenfassen:

In diesem Artikel werden hauptsächlich Inhalte zu langen Transaktionen sowie die Suche nach langen Transaktionen, der Umgang mit langen Transaktionen und die Überwachung langer Transaktionen vorgestellt. Vielleicht wissen einige von Ihnen nicht viel über Transaktionen. Ich hoffe, dieser Artikel wird Ihnen hilfreich sein. Da in diesem Artikel viele Anweisungen zu Abfragetransaktionen aufgelistet sind, werden sie wie folgt zusammengefasst:

# Alle laufenden Transaktionen und deren Laufzeit abfragen select t.*,to_seconds(now())-to_seconds(t.trx_started) idle_time from INFORMATION_SCHEMA.INNODB_TRX t \G

# Transaktionsdetails abfragen und SQL ausführen
Wählen Sie jetzt (), (UNIX_TIMESTAMP (jetzt ()) - UNIX_TIMESTAMP (a.trx_started)) diff_sec, b.id, b.user, b.host, b.db, d.SQL_TEXT aus information_schema.innodb_trx ein innerer Join information_schema.PROCESSLIST b
bei a.TRX_MYSQL_THREAD_ID=b.id und b.command = 'Sleep'
Innerer Join performance_schema.threads c ON b.id = c.PROCESSLIST_ID
innerer Join performance_schema.events_statements_current d ON d.THREAD_ID = c.THREAD_ID;

# Abfrage aller historischen SQL-Datensätze, die von der Transaktion SELECT ausgeführt wurden
 ps.id 'PROZESS-ID',
 ps.BENUTZER,
 ps.HOST,
 esh.EVENT_ID,
 trx.trx_started,
 esh.event_name 'EREIGNISNAME',
 esh.sql_text 'SQL',
 ps.Zeit 
AUS
 PERFORMANCE_SCHEMA.events_statements_history esh
 JOIN PERFORMANCE_SCHEMA.threads th ON esh.thread_id = th.thread_id
 JOIN information_schema.PROCESSLIST ps ON ps.id = th.processlist_id
 LINKS JOIN information_schema.innodb_trx trx ON trx.trx_mysql_thread_id = ps.id 
WO
 trx.trx_id IST NICHT NULL 
 UND ps.USER != 'SYSTEM_USER' 
BESTELLEN BIS
 esh.EVENT_ID;
 
 # Einfache Abfrage Transaktionssperre select * from sys.innodb_lock_waits\G
 
 #Transaktionssperrdetails abfragenSELECT
 tmp.*,
 c.SQL_Text blockierender_SQL_Text,
 p.HOST blockierender_Host 
AUS
 (
 WÄHLEN
  r.trx_state beobachtet_trx_state,
  r.trx_id warte_trx_id,
  r.trx_mysql_thread_Id Wartethread,
  r.trx_query warte_abfrage,
  b.trx_state Blockierender_trx_state,
  b.trx_id blockierende_trx_id,
  b.trx_mysql_thread_id blockierender Thread,
  b.trx_query blockierende_Abfrage 
 AUS
  information_schema.innodb_lock_waits w
  INNER JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
  INNER JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id 
 ) tmp,
 information_schema.PROCESSLIST p,
 PERFORMANCE_SCHEMA.events_statements_current c,
 PERFORMANCE_SCHEMA.threads t 
WO
 tmp.blocking_thread = p.id 
 UND t.thread_id = c.THREAD_ID 
 UND t.PROCESSLIST_ID = p.id \G

Zusammenfassen

Das Obige ist der vollständige Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels einen gewissen Lernwert für Ihr Studium oder Ihre Arbeit hat. Vielen Dank für Ihre Unterstützung von 123WORDPRESS.COM.

Das könnte Sie auch interessieren:
  • Detaillierte Erklärung von MySQL-Transaktionen und MySQL-Protokollen
  • Detaillierte Erläuterung des Implementierungsprinzips von MySQL-Transaktionen und Spring-Isolationsebenen
  • Ausführliche Erläuterung der Konzepte und Verwendung von MySQL-Transaktionen
  • Detaillierte Erläuterung der MySQL-Transaktionsverwaltungsvorgänge
  • Beispiel zum Anzeigen und Ändern der MySQL-Transaktionsisolationsebene
  • Beispielanalyse für MySQL-Transaktionen, Isolationsebenen und Sperrenverwendung
  • Detailliertes Beispiel zur Implementierung von Transaktions-Commit und -Rollback in MySQL
  • MySQL-Transaktions-Tutorial Yii2.0 Händler-Auszahlungsfunktion
  • So finden Sie Informationen zu nicht festgeschriebenen Transaktionen in MySQL
  • Detaillierte Erläuterung des Implementierungsprinzips von ACID-Transaktionen in MySQL
  • Beispiel für eine MySQL-Datenbank-übergreifende Transaktions-XA-Operation
  • Tiefgreifendes Verständnis langer MySQL-Transaktionen

<<:  Statische IP-Konfigurationsmethode für die Centos8-Brücke in der virtuellen VMware-Maschine

>>:  Lösen Sie das Problem, dass der Knotenprozess im Linux-System ausgeführt wird, aber nicht beendet werden kann

Artikel empfehlen

Erklärung zur Änderung des npm Taobao-Spiegels

1. Nutzung auf höchster Ebene 1. Installieren Sie...

Zwei Bilder von JavaScript zum Verständnis der Prototypenkette

Inhaltsverzeichnis 1. Prototyp-Beziehung 2. Proto...

Detaillierte grafische Erklärung zum Löschen des Keep-Alive-Cache

Inhaltsverzeichnis Eröffnungsszene Direktes Rende...

Beispiel einer Nginx-Standortübereinstimmungsregel

1. Grammatik Standort [=|~|~*|^~|@] /uri/ { ... }...

So implementieren Sie eine geplante Sicherung der CentOS MySQL-Datenbank

Das folgende Skript wird für die geplante Sicheru...

Vue implementiert den Anwesenheitskalender von DingTalk

In diesem Artikel wird der spezifische Code von V...

Best Practices-Handbuch für partitionierte MySQL-Tabellen

Vorwort: Partitionierung ist ein Tabellenentwurfs...

SQL IDENTITY_INSERT-Fallstudie

Wenn eine Spalte in einer Datentabelle einmal als...

Detaillierte Erklärung zur Verwendung von MySQL-Gruppenlinks

Gruppieren und Verknüpfen sind in MySQL die beide...