MySql Gap Lock Deadlock für Einsätze

8

Ich erhalte Deadlocks von Lückenschlössern auf einer Tabelle, wenn ich sie häufig aus mehreren Quellen einfüge. Hier ist eine Übersicht meiner Prozesse.

START TRANSACTION
  UPDATE vehicle_image
  SET active = 0
  WHERE vehicleID = SOMEID AND active = 1

  Loop:
    INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath
      ,vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
    VALUES (%s, %s, %s, %s, %s, %s, 1);
END TRANSACTION

Die Ausgabe von SHOW Create table vehicle_image;ist:

CREATE TABLE `vehicle_image` (
  `vehicleImageID` int(11) NOT NULL AUTO_INCREMENT,
  `vehicleID` int(11) DEFAULT NULL,
  `vehicleImageFilePath` varchar(200) DEFAULT NULL,
  `vehicleImageSplashFilePath` varchar(200) DEFAULT NULL,
  `vehicleImageThumbnailFilePath` varchar(200) DEFAULT NULL,
  `vehicleImageMiniFilePath` varchar(200) DEFAULT NULL,
  `mainVehicleImage` bit(1) DEFAULT NULL,
  `active` bit(1) DEFAULT b'1',
  `userCreated` int(11) DEFAULT NULL,
  `dateCreated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `userModified` int(11) DEFAULT NULL,
  `dateModified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`vehicleImageID`),
  KEY `active` (`active`),
  KEY `mainvehicleimage` (`mainVehicleImage`),
  KEY `vehicleid` (`vehicleID`)
) ENGINE=InnoDB AUTO_INCREMENT=22878102 DEFAULT CHARSET=latin1

Und der letzte Deadlock von SHOW engine innodb status:

LATEST DETECTED DEADLOCK
------------------------
2018-03-27 12:31:15 11a58
*** (1) TRANSACTION:
TRANSACTION 5897678083, ACTIVE 2 sec inserting
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 873570, OS thread handle 0x124bc, query id 198983754 ec2-34-239-240-179.compute-1.amazonaws.com 34.239.240.179 image_processor update
INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath, vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
VALUES (70006176, 'f180928(1)1522168276.230837full.jpg', 'f180928(1)1522168276.230837splash.jpg', 'f180928(1)1522168276.230837thumb.jpg', 'f180928(1)1522168276.230837mini.jpg', 1, 1)
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 875 page no 238326 n bits 472
  index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678083
  lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 842c365a; asc  ,6Z;;
 1: len 4; hex 815d03bc; asc  ]  ;;

*** (2) TRANSACTION:
TRANSACTION 5897678270, ACTIVE 1 sec inserting, thread declared inside InnoDB 5000
mysql tables in use 1, locked 1
3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
MySQL thread id 873571, OS thread handle 0x11a58, query id 198983849 ec2-35-171-169-21.compute-1.amazonaws.com 35.171.169.21 image_processor update
INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath, vehicleImageSplashFilePath, vehicleImageThumbnailFilePath, vehicleImageMiniFilePath, mainVehicleImage, active)
VALUES (70006326, '29709(1)1522168277.4443843full.jpg', '29709(1)1522168277.4443843splash.jpg', '29709(1)1522168277.4443843thumb.jpg', '29709(1)1522168277.4443843mini.jpg', 1, 1)
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 875 page no 238326 n bits 464
  index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678270
  lock_mode X locks gap before rec
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 842c365a; asc  ,6Z;;
 1: len 4; hex 815d03bc; asc  ]  ;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 875 page no 238326 n bits 472
  index `vehicleid` of table `ipacket`.`vehicle_image` trx id 5897678270
  lock_mode X locks gap before rec insert intention waiting
Record lock, heap no 378 PHYSICAL RECORD: n_fields 2; compact format; info bits 0
 0: len 4; hex 842c365a; asc  ,6Z;;
 1: len 4; hex 815d03bc; asc  ]  ;;

*** WE ROLL BACK TRANSACTION (2)

Ich führe viele dieser Prozesse gleichzeitig aus, aber niemals zwei Prozesse, die denselben verwenden VehicleID. Ich bin wirklich verwirrt, warum ich Deadlocks bekomme.

Ich habe das Problem vorübergehend mithilfe der Isolationsstufe gelöst READ COMMITTED, aber ich habe gelesen, dass dies Änderungen an der Replikation erfordert, da Sie die Replikation auf Zeilenebene durchführen müssen.

Ich habe hier andere Fragen gelesen, die meinen ähnlich sind, aber ich bin etwas neu in SQL und kann immer noch nicht verstehen, warum dies geschieht.

Ähnliche Fragen:
- Deadlock bei MySQL-Insert-Anweisungen
- MySQL InnoDB Deadlock Für 2 einfache Insert-Abfragen

AKTUALISIEREN:

Ich fand heraus, dass die Verwendung READ COMMITTEDdas Problem nicht wirklich gelöst hat. Ich habe immer noch nicht herausgefunden, warum die Deadlocks auftreten, und ich weiß wirklich nicht, wie ich weiter diagnostizieren soll, als ich es derzeit getan habe. Ich bekomme weiterhin Deadlocks in mein Produktionssystem. Jede Hilfe wäre dankbar.

Brian Sizemore
quelle
Könnten Sie uns bitte etwas mehr Details geben - insbesondere: Festplattenkonfiguration, Anzahl der Platten, Leistungsmerkmale? RAM, wie viel? CPU, Anzahl und Leistung? Anzahl der Transaktionen pro Sekunde, pro Minute, pro Stunde und pro Tag? Variieren diese Preise im Laufe der Zeit? Wie genau wirken sich diese Deadlocks auf die Leistung aus? Geben Sie uns die Ausgabe von SHOW PROCESSLIST;. Meistens REPEATABLE READist dies die beste Isolationsstufe für die meisten Apps, daher würde ich mir keine Sorgen um die Verwendung machen. Gab es eine spürbare Leistungssteigerung, als Sie die Standardeinstellung geändert haben REPEATABLE READ?
Vérace
Wie kann das funktionieren? Sie haben nicht zitierte Zeichenfolgen VARCHARs.
Rick James
Wo ist die END LOOP?
Rick James
@RickJames Ich habe keine nicht zitierten Zeichenfolgen in VARCHARS, die Abfragen funktionieren wie erwartet, wenn sie in 95% der Fälle ausgeführt werden. Die END LOOP wird durch das Zurückkehren auf die gleiche Ebene angezeigt. ZB Die Schleife beginnt, ich führe diese Einfügeanweisung mehrmals aus, dann endet die Schleife und die Transaktion endet. Beachten Sie, dass dies loopnur ein Pseudocode ist, um darzustellen, was gerade stattfindet.
Brian Sizemore
@ Vérace Repeatable Read ist die Standardeinstellung für diese Tabelle (unter Verwendung der innodb-Engine). Getestet habe ich es tatsächlich ändert repeatable readan read committeddem eine untere Isolationsstufe dann wiederholbare lesen, aber leider hat es nicht die Deadlocks zu stoppen. Ich weiß, dass die Hardware den Server beeinflusst (es ist eine ec2-Instanz, ich müsste nach Einzelheiten suchen), aber ich denke nicht, dass diese Informationen notwendig wären, um zu verstehen, warum die Deadlocks auftreten. Die sporadische Natur macht es auch schwierig, die Ausgabe der Show-Prozessliste zu erfassen. wenn der Deadlock auftritt.
Brian Sizemore

Antworten:

4

Ich bin kein MySQL-Experte, aber nach dem Aussehen Ihrer Deadlock-Protokolle muss die gesamte Datenseite (238326) des VehicleIDnicht gruppierten Index gesperrt werden , obwohl Sie pro Anweisung unterschiedliche Fahrzeug-IDs einfügen .

Die Tatsache, dass Sie gelegentlich Deadlocks erhalten, bedeutet, dass Sie innerhalb einer Seite mehrere Fahrzeug-IDs haben. Daher besteht eine geringe Wahrscheinlichkeit, dass zwei verschiedene Prozesse eine Sperre für dieselbe Seite benötigen.

Am besten raten Sie , Ihre Transaktionen so klein wie möglich zu halten .

Wenn Sie auf folgende Weise Folgendes tun können, verringert sich die Wahrscheinlichkeit eines Deadlocks:

START TRANSACTION;
  UPDATE vehicle_image SET active = 0 WHERE vehicleID = SOMEID and active = 1;
END TRANSACTION;
Loop:
  START TRANSACTION;
  INSERT INTO vehicle_image (vehicleID, vehicleImageFilePath,
    vehicleImageSplashFilePath, vehicleImageThumbnailFilePath,
    vehicleImageMiniFilePath, mainVehicleImage, active)
  VALUES (%s, %s, %s, %s, %s, %s, 1);  
  END TRANSACTION;
--EndLoop here

Wenn Sie können, versuchen Sie, den Füllfaktor dieses Index auf 95% zu ändern , und testen Sie, ob Sie weniger Deadlocks erhalten.

Ein extremerer Test wäre , diesen Index beim EINFÜGEN vollständig zu entfernen und ihn anschließend neu zu erstellen .

Oreo
quelle
Haben Sie einen Einblick, warum die gesamte Seite gegen nur die Zeilen gesperrt wird, die ich einfügen möchte?
Brian Sizemore
1
Außerdem werde ich meinen Code ein wenig umgestalten und die Transaktionszeit verkürzen. Ich glaube, Sie haben Recht, dass es einen signifikanten Unterschied machen sollte.
Brian Sizemore
Ich bin mir nicht sicher, wie die Interna von MySQL funktionieren, aber diese Antwort erklärt es für MS SQL. Einige gute MySQL-Tipps auch im MySQL-Handbuch .
Oreo
1
MySQL bietet keine Kontrolle über den Füllfaktor.
Rick James
2
Nachdem ich meinen Code so umgestaltet habe, dass meine Einfügungen und die Update-Anweisung in die Warteschlange gestellt und sehr nahe beieinander ausgeführt werden, ist mein Problem behoben. Darüber hinaus konnte ich dies weiter skalieren (ungefähr doppelt so viele parallele Prozesse wie zuvor) und es funktioniert immer noch korrekt. Danke Oreo!
Brian Sizemore
1

MySQL sperrt nicht nur die betroffene Zeile, sondern auch die betroffene Indexzeile und die Lücke zwischen den Indexzeilen (wie hier beschrieben ). Da Primärschlüssel immer indiziert sind und Sie sie in Ihren Aktualisierungen verwenden, vermute ich, dass mehrere Transaktionen, die versuchen, mehrere Zeilen zu aktualisieren, zu überlappenden Indexlückensperren führen, die wiederum den Deadlock verursachen.

Um dies zu beheben, empfehle ich auch den Rat von Oreos, eine Transaktion so klein wie möglich zu halten. Wenn die aktualisierten Zeilen unabhängig voneinander sind, sollten Sie für jede Zeile eine separate Transaktion verwenden.

Flourid
quelle