web-dev-qa-db-fra.com

MySql Gap Lock Deadlock sur les inserts

J'obtiens des verrous mortels à partir de verrous d'espacement sur une table lorsque j'insère fréquemment des sources multiples. Voici un aperçu de mes processus.

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

La sortie de SHOW Create table vehicle_image; est:

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

Et le dernier Deadlock donné par 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)

J'exécute plusieurs de ces processus simultanément, mais je n'exécute jamais deux processus utilisant le même VehicleID. Je ne comprends vraiment pas pourquoi je reçois des blocages.

J'ai temporairement résolu le problème en utilisant le niveau d'isolement READ COMMITTED, mais j'ai lu que cela nécessite des modifications de réplication dans la mesure où vous devez effectuer une réplication au niveau des lignes.

J'ai lu d'autres questions ici qui sont similaires aux miennes, mais je suis un peu nouveau pour SQL et je ne comprends toujours pas pourquoi cela se produit.

Questions similaires:
- Blocage sur les relevés d'insertion MySQL
- MySQL InnoDB Deadlock Pour 2 requêtes d'insertion simples

MISE À JOUR:

J'ai découvert qu'en utilisant READ COMMITTED n'a pas réellement résolu le problème. Je n'ai toujours pas compris pourquoi les blocages se produisent et je ne sais vraiment pas comment diagnostiquer plus loin que ce que j'ai actuellement. Je continue d'avoir des impasses dans mon système de production. Toute aide serait appréciée.

8
Brian Sizemore

Je ne suis pas un expert MySQL, mais d'après l'apparence de vos journaux Deadlock, même si vous insérez différents ID de véhicule par instruction, ceux-ci nécessitent la page de données entière (238326) de la VehicleID non index cluster à verrouiller.

Le fait que vous obteniez occasionnellement des blocages signifie que sur 1 page, vous avez plusieurs ID de véhicule, il y a donc une petite chance que 2 processus différents aient besoin d'un verrou pour la même page.

La meilleure chose à conseiller est de garder vos transactions aussi petites que possible.

S'il existe un moyen de procéder comme suit, cela contribuera à réduire les risques de blocage:

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

Si vous le pouvez, essayez de changer le facteur de remplissage de cet index à 95%, et testez pour voir si vous obtenez moins de blocages.

Un test plus extrême serait de supprimer cet index complètement en INSÉRANT, puis recréer une fois terminé.

4
Oreo

MySQL verrouille non seulement la ligne affectée, mais également la ligne d'index affectée et l'écart entre les lignes d'index (comme décrit ici ). Étant donné que les clés primaires sont toujours indexées et que vous les utilisez dans vos mises à jour, je soupçonne que plusieurs transactions essayant de mettre à jour plusieurs lignes entraînent chacune des verrous d'écart d'index qui se chevauchent, ce qui crée à son tour le blocage.

Pour résoudre ce problème, je recommande également les conseils d'Oreos pour maintenir une transaction aussi petite que possible. Si les lignes mises à jour sont indépendantes les unes des autres, vous devez utiliser une transaction distincte pour chacune d'entre elles.

1
Flourid