Wie kann ich debuggen, dass das Wartezeitlimit für Sperren unter MySQL überschritten wurde?

269

In meinen Produktionsfehlerprotokollen sehe ich gelegentlich:

SQLSTATE [HY000]: Allgemeiner Fehler: 1205 Wartezeit für Sperren überschritten; Versuchen Sie, die Transaktion neu zu starten

Ich weiß, welche Abfrage gerade versucht, auf die Datenbank zuzugreifen, aber gibt es eine Möglichkeit herauszufinden, welche Abfrage genau zu diesem Zeitpunkt gesperrt war?

Matt McCormick
quelle
1
Ich empfehle jedem
dringend

Antworten:

261

Was dies verrät, ist das Wort Transaktion . Aus der Aussage geht hervor, dass die Abfrage versucht hat, mindestens eine Zeile in einer oder mehreren InnoDB-Tabellen zu ändern.

Da Sie die Abfrage kennen, sind alle Tabellen, auf die zugegriffen wird, Kandidaten für den Schuldigen.

Von dort sollten Sie laufen können SHOW ENGINE INNODB STATUS\G

Sie sollten in der Lage sein, die betroffenen Tabellen zu sehen.

Sie erhalten alle Arten von zusätzlichen Sperr- und Mutex-Informationen.

Hier ist ein Beispiel von einem meiner Kunden:

mysql> show engine innodb status\G
*************************** 1. row ***************************
  Type: InnoDB
  Name:
Status:
=====================================
110514 19:44:14 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 4 seconds
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 9014315, signal count 7805377
Mutex spin waits 0, rounds 11487096053, OS waits 7756855
RW-shared spins 722142, OS waits 211221; RW-excl spins 787046, OS waits 39353
------------------------
LATEST FOREIGN KEY ERROR
------------------------
110507 21:41:35 Transaction:
TRANSACTION 0 606162814, ACTIVE 0 sec, process no 29956, OS thread id 1223895360 updating or deleting, thread declared inside InnoDB 499
mysql tables in use 1, locked 1
14 lock struct(s), heap size 3024, 8 row lock(s), undo log entries 1
MySQL thread id 3686635, query id 124164167 10.64.89.145 viget updating
DELETE FROM file WHERE file_id in ('6dbafa39-7f00-0001-51f2-412a450be5cc' )
Foreign key constraint fails for table `backoffice`.`attachment`:
,
  CONSTRAINT `attachment_ibfk_2` FOREIGN KEY (`file_id`) REFERENCES `file` (`file_id`)
Trying to delete or update in parent table, in index `PRIMARY` tuple:
DATA TUPLE: 17 fields;
 0: len 36; hex 36646261666133392d376630302d303030312d353166322d343132613435306265356363; asc 6dbafa39-7f00-0001-51f2-412a450be5cc;; 1: len 6; hex 000024214f7e; asc   $!O~;; 2: len 7; hex 000000400217bc; asc    @   ;; 3: len 2; hex 03e9; asc   ;; 4: len 2; hex 03e8; asc   ;; 5: len 36; hex 65666635323863622d376630302d303030312d336632662d353239626433653361333032; asc eff528cb-7f00-0001-3f2f-529bd3e3a302;; 6: len 40; hex 36646234376337652d376630302d303030312d353166322d3431326132346664656366352e6d7033; asc 6db47c7e-7f00-0001-51f2-412a24fdecf5.mp3;; 7: len 21; hex 416e67656c73204e6f7720436f6e666572656e6365; asc Angels Now Conference;; 8: len 34; hex 416e67656c73204e6f7720436f6e666572656e6365204a756c7920392c2032303131; asc Angels Now Conference July 9, 2011;; 9: len 1; hex 80; asc  ;; 10: len 8; hex 8000124a5262bdf4; asc    JRb  ;; 11: len 8; hex 8000124a57669dc3; asc    JWf  ;; 12: SQL NULL; 13: len 5; hex 8000012200; asc    " ;; 14: len 1; hex 80; asc  ;; 15: len 2; hex 83e8; asc   ;; 16: len 4; hex 8000000a; asc     ;;

But in child table `backoffice`.`attachment`, in index `PRIMARY`, there is a record:
PHYSICAL RECORD: n_fields 6; compact format; info bits 0
 0: len 30; hex 36646261666133392d376630302d303030312d353166322d343132613435; asc 6dbafa39-7f00-0001-51f2-412a45;...(truncated); 1: len 30; hex 38666164663561652d376630302d303030312d326436612d636164326361; asc 8fadf5ae-7f00-0001-2d6a-cad2ca;...(truncated); 2: len 6; hex 00002297b3ff; asc   "   ;; 3: len 7; hex 80000040070110; asc    @   ;; 4: len 2; hex 0000; asc   ;; 5: len 30; hex 416e67656c73204e6f7720436f6e666572656e636520446f63756d656e74; asc Angels Now Conference Document;;

------------
TRANSACTIONS
------------
Trx id counter 0 620783814
Purge done for trx's n:o < 0 620783800 undo n:o < 0 0
History list length 35
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1192212800
MySQL thread id 5341758, query id 189708501 127.0.0.1 lwdba
show innodb status
---TRANSACTION 0 620783788, not started, process no 29956, OS thread id 1196472640
MySQL thread id 5341773, query id 189708353 10.64.89.143 viget
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1223895360
MySQL thread id 5341667, query id 189706152 10.64.89.145 viget
---TRANSACTION 0 0, not started, process no 29956, OS thread id 1227888960
MySQL thread id 5341556, query id 189699857 172.16.135.63 lwdba
---TRANSACTION 0 620781112, not started, process no 29956, OS thread id 1222297920
MySQL thread id 5341511, query id 189696265 10.64.89.143 viget
---TRANSACTION 0 620783736, not started, process no 29956, OS thread id 1229752640
MySQL thread id 5339005, query id 189707998 10.64.89.144 viget
---TRANSACTION 0 620783785, not started, process no 29956, OS thread id 1198602560
MySQL thread id 5337583, query id 189708349 10.64.89.145 viget
---TRANSACTION 0 620783469, not started, process no 29956, OS thread id 1224161600
MySQL thread id 5333500, query id 189708478 10.64.89.144 viget
---TRANSACTION 0 620781240, not started, process no 29956, OS thread id 1198336320
MySQL thread id 5324256, query id 189708493 10.64.89.145 viget
---TRANSACTION 0 617458223, not started, process no 29956, OS thread id 1195141440
MySQL thread id 736, query id 175038790 Has read all relay log; waiting for the slave I/O thread to update it
--------
FILE I/O
--------
I/O thread 0 state: waiting for i/o request (insert buffer thread)
I/O thread 1 state: waiting for i/o request (log thread)
I/O thread 2 state: waiting for i/o request (read thread)
I/O thread 3 state: waiting for i/o request (write thread)
Pending normal aio reads: 0, aio writes: 0,
 ibuf aio reads: 0, log i/o's: 0, sync i/o's: 0
Pending flushes (fsync) log: 0; buffer pool: 0
519878 OS file reads, 18962880 OS file writes, 13349046 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 6.25 writes/s, 4.50 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 1190, seg size 1192,
174800 inserts, 174800 merged recs, 54439 merges
Hash table size 35401603, node heap has 35160 buffer(s)
0.50 hash searches/s, 11.75 non-hash searches/s
---
LOG
---
Log sequence number 28 1235093534
Log flushed up to   28 1235093534
Last checkpoint at  28 1235091275
0 pending log writes, 0 pending chkp writes
12262564 log i/o's done, 3.25 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total memory allocated 18909316674; in additional pool allocated 1048576
Dictionary memory allocated 2019632
Buffer pool size   1048576
Free buffers       175763
Database pages     837653
Modified db pages  6
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages read 770138, created 108485, written 7795318
0.00 reads/s, 0.00 creates/s, 4.25 writes/s
Buffer pool hit rate 1000 / 1000
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
1 read views open inside InnoDB
Main thread process no. 29956, id 1185823040, state: sleeping
Number of rows inserted 6453767, updated 4602534, deleted 3638793, read 388349505551
0.25 inserts/s, 1.25 updates/s, 0.00 deletes/s, 2.75 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================

1 row in set, 1 warning (0.00 sec)

Sie sollten in Betracht ziehen, den Wert für das Wartezeitlimit für die Sperre für InnoDB zu erhöhen , indem Sie das Zeitlimit für innodb_lock_wait_timeout festlegen . Der Standardwert beträgt 50 Sekunden

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50    |
+--------------------------+-------+
1 row in set (0.01 sec)

/etc/my.cnfMit dieser Zeile können Sie dauerhaft einen höheren Wert einstellen

[mysqld]
innodb_lock_wait_timeout=120

und starte mysql neu. Wenn Sie MySQL derzeit nicht neu starten können, führen Sie Folgendes aus:

SET GLOBAL innodb_lock_wait_timeout = 120; 

Sie können es auch einfach für die Dauer Ihrer Sitzung einstellen

SET innodb_lock_wait_timeout = 120; 

gefolgt von Ihrer Anfrage

RolandoMySQLDBA
quelle
5
Für die integrierte InnoDB kann die innodb_lock_wait_timeoutVariable nur beim Serverstart festgelegt werden. Das InnoDB-Plugin kann beim Start festgelegt oder zur Laufzeit geändert werden und hat sowohl globale als auch Sitzungswerte.
Timo Huovinen
1
Hallo @rolandomysqldba, können Sie mir bitte einen Vorschlag für diesen Beitrag machen: stackoverflow.com/questions/18267565/…
Manish Sapkal
2
Ich erhalte diesen Fehler, wenn ich versuche, die erste Abfrage SQL Error (1064): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\G' at line 1
auszuführen
1
@Pacerier Jedes Mal, wenn mysqld neu gestartet wird, müssen Sie SET GLOBAL innodb_lock_wait_timeout = 120;erneut ausführen . Wenn /etc/my.cnfdie Option hat, innodb_lock_wait_timeoutwird für Sie eingestellt. Nicht jeder hat das SUPER-Privileg, es global für alle anderen zu ändern ( dev.mysql.com/doc/refman/5.6/en/… )
RolandoMySQLDBA
3
@IulianOnofrei Das Zeichen \ G ist eine Besonderheit der MySQL-Befehlszeile und ändert die Art und Weise, wie die Ausgabe angezeigt wird. Verwenden Sie für andere MySQL-Clients stattdessen einfach ein reguläres Semikolon.
thenickdude
83

Wie jemand in einem der vielen SO-Threads zu diesem Problem erwähnt hat: Manchmal wird der Prozess, der die Tabelle gesperrt hat, in der Prozessliste als schlafend angezeigt! Ich riss mir die Haare aus, bis ich alle schlafenden Fäden getötet hatte, die in der fraglichen Datenbank geöffnet waren (zu diesem Zeitpunkt waren keine aktiv). Damit wurde die Tabelle endlich entsperrt und die Update-Abfrage ausgeführt.

Der Kommentator sagte etwas Ähnliches wie "Manchmal sperrt ein MySQL-Thread eine Tabelle und schläft dann, während er darauf wartet, dass etwas passiert, das nicht mit MySQL zu tun hat."

Nachdem show engine innodb statusich das Protokoll erneut überprüft hatte (nachdem ich den für die Sperre verantwortlichen Client aufgespürt hatte), stellte ich fest, dass der betreffende festsitzende Thread ganz unten in der Transaktionsliste unter den aktiven Abfragen aufgeführt war, die kurz vor dem Fehler standen wegen des eingefrorenen Schlosses raus:

------------------
---TRANSACTION 2744943820, ACTIVE 1154 sec(!!)
2 lock struct(s), heap size 376, 2 row lock(s), undo log entries 1
MySQL thread id 276558, OS thread handle 0x7f93762e7710, query id 59264109 [ip] [database] cleaning up
Trx read view will not see trx with id >= 2744943821, sees < 2744943821

(Unsicher, ob die Nachricht "Trx-Leseansicht" mit der eingefrorenen Sperre zusammenhängt, aber im Gegensatz zu den anderen aktiven Transaktionen wird diese nicht mit der ausgegebenen Abfrage angezeigt und behauptet stattdessen, die Transaktion sei "bereinigen", hat jedoch mehrere Reihenschlösser)

Die Moral der Geschichte ist, dass eine Transaktion aktiv sein kann, obwohl der Thread schläft.

Eric L.
quelle
2
Ich kann nicht sagen, dass du mein Leben gerettet hast, aber du hast mich mit Sicherheit auf Frieden konzentriert. Als ich Ihre Antwort las, fand ich einen gruseligen Thread, der 3260 Sekunden lang aktiv ist und nirgendwo auftaucht. Nachdem ich es getötet hatte, waren alle meine Probleme gelöst!
kommradHomer
Das war mein Problem. Eine Sleeping-Transaktion mit einer Zeit von 20.000 Sekunden, die verhinderte, dass der verzögerte Job in einer Rails-App ordnungsgemäß ausgeführt wurde. Danke @Eirik
bigtex777
Irgendeine Idee, warum eine schlafende Transaktion sowieso nicht getötet wird? Gibt es eine Zeitüberschreitung, innerhalb derer eine Transaktion abgeschlossen werden muss?
Patrickdavey
1
Andere Befehle, die bei der Suche nach Sperrtransaktionen hilfreich sein können: show processlist;Anzeigen einer vollständigen Liste der derzeit ausgeführten Prozesse. Dies ist hilfreich , da es sich um eine komprimierte Version von handelt show engine innodb status\g. Wenn sich Ihre Datenbank auf einer Amazon RDS-Instanz befindet, können Sie auch CALL mysql.rds_kill(<thread_id>);Threads beenden. Ich denke, es hat höhere Berechtigungen, weil es mir erlaubt hat, mehr Prozesse als einfach zu kill <thread_id>;
beenden
1
Hat jemand eine Quelle dafür - vielleicht eine Dokumentationsseite, auf der angegeben ist, dass die Sperren vor der COMMIT-Phase platziert sind? Ich konnte nichts finden, obwohl ich genau dieses Problem sah und es durch das Töten des schlafenden Fadens, der die Schlösser hielt, geklärt wurde.
Erin Schoonover
42

Aufgrund der Beliebtheit von MySQL ist es kein Wunder, dass das Wartezeitlimit für Sperren überschritten wurde. Wenn Sie versuchen, die Transaktionsausnahme neu zu starten, wird SO so viel Aufmerksamkeit geschenkt.

Je mehr Konflikte Sie haben, desto größer ist die Wahrscheinlichkeit von Deadlocks, die eine DB-Engine durch Timeout einer der Deadlock-Transaktionen auflöst. Außerdem führen Transaktionen mit langer Laufzeit, bei denen eine große Anzahl von Einträgen geändert wurde (z. B. UPDATEoder DELETE) Sperren (um Sperren zu vermeiden, um Anomalien beim Dirty-Write zu vermeiden, wie im High-Performance Java Persistence- Buch erläutert ), eher zu Konflikten mit anderen Transaktionen.

Obwohl InnoDB MVCC, können Sie mithilfe der FOR UPDATEKlausel weiterhin explizite Sperren anfordern . Im Gegensatz zu anderen gängigen DBs (Oracle, MSSQL, PostgreSQL, DB2) wird MySQL jedoch REPEATABLE_READals Standardisolationsstufe verwendet .

Jetzt werden die Sperren, die Sie erworben haben (entweder durch Ändern von Zeilen oder durch explizites Sperren), für die Dauer der aktuell ausgeführten Transaktion gehalten. Wenn Sie eine gute Erklärung für den Unterschied zwischen REPEATABLE_READund READ COMMITTEDin Bezug auf das Sperren wünschen, lesen Sie bitte diesen Percona-Artikel .

In REPEATABLE READ wird jede während einer Transaktion erworbene Sperre für die Dauer der Transaktion gehalten.

In READ COMMITTED werden die Sperren, die nicht mit dem Scan übereinstimmen, nach Abschluss der STATEMENT freigegeben.

...

Dies bedeutet, dass in READ COMMITTED andere Transaktionen Zeilen aktualisieren können, die sie nach Abschluss der UPDATE-Anweisung nicht aktualisieren konnten (in REPEATABLE READ).

Deshalb: Je restriktiver die Isolationsstufe ( REPEATABLE_READ,SERIALIZABLE ) ist, größer ist die Wahrscheinlichkeit eines Deadlocks. Dies ist kein Problem "per se", es ist ein Kompromiss.

Sie können sehr gute Ergebnisse erzielen READ_COMMITED, da Sie bei Verwendung logischer Transaktionen, die sich über mehrere HTTP-Anforderungen erstrecken, eine Verhinderung verlorener Updates auf Anwendungsebene benötigen . Der optimistische Sperransatz zielt auf verlorene Aktualisierungen ab , die auch dann auftreten können, wenn Sie die SERIALIZABLEIsolationsstufe verwenden und gleichzeitig den Sperrkonflikt verringern, indem Sie die Verwendung zulassen READ_COMMITED.

Vlad Mihalcea
quelle
4
Ist das Wartezeitlimit für Sperren nicht anders als für Deadlocks? Wenn beispielsweise ein Thread aus legitimen Gründen eine Sperre von 60 Sekunden hält, kann das Wartezeitlimit für die Sperre auftreten. Stimmt es nicht, dass MySQL dies erkennt und eine Transaktion sofort beendet, wenn es wirklich einen Deadlock gibt, und dies nicht mit dem Wartezeitlimit für Sperren zusammenhängt?
ColinM
1
Du hast recht. Die Datenbank erkennt die Deadlock nach dem Timeout und beendet einen Wartevorgang, sodass eine Transaktion gewinnt, während die andere fehlschlägt. Aber je länger Sie eine Sperre halten, desto weniger skalierbar wird die Anwendung. Selbst wenn Sie nicht auf Deadlocks stoßen, erhöhen Sie den serialisierbaren Teil Ihres Anwendungslaufzeitverhaltens.
Vlad Mihalcea
19

Für den Datensatz tritt die Ausnahme des Wartezeitlimits für Sperren auch dann auf, wenn ein Deadlock vorliegt und MySQL ihn nicht erkennen kann, sodass nur eine Zeitüberschreitung auftritt. Ein weiterer Grund könnte eine extrem lange laufende Abfrage sein, die jedoch leichter zu lösen / zu reparieren ist, und ich werde diesen Fall hier nicht beschreiben.

MySQL ist normalerweise in der Lage, Deadlocks zu verarbeiten, wenn sie innerhalb von zwei Transaktionen "richtig" erstellt wurden. MySQL beendet / rollt dann nur die eine Transaktion, die weniger Sperren besitzt (ist weniger wichtig, da dies weniger Zeilen betrifft) und lässt die andere beenden.

Nehmen wir nun an, es gibt zwei Prozesse A und B und 3 Transaktionen:

Process A Transaction 1: Locks X
Process B Transaction 2: Locks Y
Process A Transaction 3: Needs Y => Waits for Y
Process B Transaction 2: Needs X => Waits for X
Process A Transaction 1: Waits for Transaction 3 to finish

(see the last two paragraph below to specify the terms in more detail)

=> deadlock 

Dies ist eine sehr unglückliche Einrichtung, da MySQL keinen Deadlock erkennen kann (innerhalb von 3 Transaktionen). Also, was MySQL macht, ist ... nichts! Es wartet nur, da es nicht weiß, was zu tun ist. Es wird gewartet, bis die erste erfasste Sperre das Zeitlimit überschreitet (Prozess A, Transaktion 1: Sperren X). Dadurch wird die Sperre X entsperrt, wodurch Transaktion 2 usw. entsperrt wird.

Die Kunst besteht darin herauszufinden, was (welche Abfrage) die erste Sperre (Sperre X) verursacht. Sie können leicht sehen ( show engine innodb status), dass Transaktion 3 auf Transaktion 2 wartet, aber Sie werden nicht sehen, auf welche Transaktion Transaktion 2 wartet (Transaktion 1). MySQL druckt keine mit Transaktion 1 verbundenen Sperren oder Abfragen. Der einzige Hinweis ist, dass ganz unten in der Transaktionsliste (dershow engine innodb status Ausdrucks) Transaktion 1 anscheinend nichts tut (sondern tatsächlich auf Transaktion 3 wartet) Fertig).

Hier wird beschrieben, wie ermittelt wird, durch welche SQL-Abfrage die Sperre (Lock X) für eine bestimmte wartende Transaktion gewährt wird Tracking MySQL query history in long running transactions

Wenn Sie sich fragen, was der Prozess und die Transaktion genau im Beispiel sind. Der Prozess ist ein PHP-Prozess. Transaktion ist eine Transaktion im Sinne von innodb-trx-table . In meinem Fall hatte ich zwei PHP-Prozesse, in denen ich jeweils eine Transaktion manuell gestartet habe. Der interessante Teil war, dass MySQL, obwohl ich eine Transaktion in einem Prozess gestartet habe, intern zwei separate Transaktionen verwendet hat (ich habe keine Ahnung warum, vielleicht kann es ein MySQL-Entwickler erklären).

MySQL verwaltet seine eigenen Transaktionen intern und hat (in meinem Fall) beschlossen, zwei Transaktionen zu verwenden, um alle SQL-Anforderungen zu verarbeiten, die vom PHP-Prozess stammen (Prozess A). Die Aussage, dass Transaktion 1 auf den Abschluss von Transaktion 3 wartet, ist eine interne MySQL-Sache. MySQL "wusste", dass Transaktion 1 und Transaktion 3 tatsächlich als Teil einer "Transaktions" -Anforderung (aus Prozess A) instanziiert wurden. Jetzt wurde die gesamte "Transaktion" blockiert, da Transaktion 3 (ein Teil von "Transaktion") blockiert wurde. Da "Transaktion" die Transaktion 1 nicht beenden konnte (auch ein Teil der "Transaktion"), wurde sie ebenfalls als nicht abgeschlossen markiert. Dies ist, was ich mit "Transaktion 1 wartet auf den Abschluss von Transaktion 3" gemeint habe.

Tomas Bilka
quelle
14

Das große Problem bei dieser Ausnahme ist, dass sie in einer Testumgebung normalerweise nicht reproduzierbar ist und wir nicht in der Lage sind, den Status der Innodb-Engine auszuführen, wenn dies auf einem Produkt geschieht. In einem der Projekte habe ich den folgenden Code für diese Ausnahme in einen Catch-Block eingefügt. Das hat mir geholfen, den Motorstatus zu ermitteln, als die Ausnahme auftrat. Das hat sehr geholfen.

Statement st = con.createStatement();
ResultSet rs =  st.executeQuery("SHOW ENGINE INNODB STATUS");
while(rs.next()){
    log.info(rs.getString(1));
    log.info(rs.getString(2));
    log.info(rs.getString(3));
}
Maruthi
quelle
11

Schauen Sie sich die Manpage des pt-deadlock-loggerDienstprogramms an :

brew install percona-toolkit
pt-deadlock-logger --ask-pass server_name

Es extrahiert Informationen aus den engine innodb statusoben genannten und kann auch verwendet werden, um eine zu erstellen, daemondie alle 30 Sekunden ausgeführt wird.

Andrei Sura
quelle
3
Dieses Tool ist jetzt Teil des Percona-Toolkits
Brad Mace
Wartezeiten für Sperren sind nicht dasselbe wie Deadlocks, insbesondere zeigt innodb keine Informationen über sie an, da sie keine erkannten Deadlocks sind. Daher denke ich nicht, dass pt-deadlock-logger eine Hilfe ist.
Jay Paroline
Sperrzeitlimits und Deadlocks hängen jedoch zusammen - siehe dev.mysql.com/doc/refman/5.7/de/innodb-deadlock-detection.html
Andrei Sura
10

Hier ist, was ich letztendlich tun musste, um herauszufinden, welche "andere Abfrage" das Problem mit dem Sperrzeitlimit verursacht hat. Im Anwendungscode verfolgen wir alle ausstehenden Datenbankaufrufe in einem separaten Thread, der dieser Aufgabe gewidmet ist. Wenn ein DB-Aufruf länger als N Sekunden dauert (für uns sind es 30 Sekunden), protokollieren wir:

-- Pending InnoDB transactions
SELECT * FROM information_schema.innodb_trx ORDER BY trx_started; 

-- Optionally, log what transaction holds what locks
SELECT * FROM information_schema.innodb_locks;

Mit oben konnten wir gleichzeitige Abfragen lokalisieren, die die Zeilen sperrten, die den Deadlock verursachten. In meinem Fall handelte es sich um Anweisungen, INSERT ... SELECTdie im Gegensatz zu einfachen SELECTs die zugrunde liegenden Zeilen sperren. Sie können den Code dann neu organisieren oder eine andere Transaktionsisolation verwenden, z. B. "Nicht festgeschrieben".

Viel Glück!

Slawomir
quelle
10

Aus Rolandos Antwort oben extrapoliert, blockieren diese Ihre Anfrage:

---TRANSACTION 0 620783788, not started, process no 29956, OS thread id 1196472640
MySQL thread id 5341773, query id 189708353 10.64.89.143 viget

Wenn Sie Ihre Abfrage ausführen müssen und nicht warten können, bis die anderen ausgeführt werden, beenden Sie sie mit der MySQL-Thread-ID:

kill 5341773 <replace with your thread id>

(aus MySQL heraus, natürlich nicht aus der Shell)

Sie müssen die Thread-IDs finden aus:

show engine innodb status\G

Befehl, und finden Sie heraus, welcher die Datenbank blockiert.

Ellert van Koperen
quelle
1
Woher weißt du das 5341773? Ich sehe nicht, was das eine von den anderen unterscheidet.
Wodin
Nein, es ist wahrscheinlich nicht diese threadID, es war ein Beispiel. Sie müssen die Thread-IDs im Befehl "show engine innodb status \ G" finden und herausfinden, welche die Datenbank blockiert.
Ellert van Koperen
1
Vielen Dank. Mit anderen Worten, es gibt keine Möglichkeit zu sagen, um welches es sich handelt, ohne sie z. B. einzeln zu töten?
Wodin
In der Liste der Transaktionen können Sie sehen, welche wie lange ausgeführt werden. Sie müssen sie also nicht einzeln töten. Diese Liste gibt Ihnen normalerweise eine ziemlich gute Vorstellung davon, was los ist.
Ellert van Koperen
8

Sie können verwenden:

show full processlist

Hier werden alle Verbindungen in MySQL und der aktuelle Verbindungsstatus sowie die ausgeführte Abfrage aufgelistet. Es gibt auch eine kürzere Variante, show processlist;die die abgeschnittene Abfrage sowie die Verbindungsstatistiken anzeigt.

Gerrit Brink
quelle
-2

Aktivieren Sie MySQL general.log (festplattenintensiv) und verwenden Sie mysql_analyse_general_log.pl , um lang laufende Transaktionen zu extrahieren, z.

--min-duration = Ihr Wert für innodb_lock_wait_timeout

Deaktivieren Sie danach general.log.

mick
quelle