web-dev-qa-db-ja.com

挿入時のMySqlギャップロックデッドロック

複数のソースから頻繁に挿入すると、テーブルのギャップロックからデッドロックが発生します。これが私のプロセスの概要です。

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

SHOW Create table vehicle_image;の出力は次のとおりです。

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

そして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)

これらのプロセスの多くを同時に実行していますが、同じVehicleIDを使用している2つのプロセスを実行していません。デッドロックが発生する理由について、私は本当に混乱しています。

私は一時的に分離レベルREAD COMMITTEDを使用して問題を解決しましたが、これにはレプリケーションを変更する必要があることを読みました。行レベルのレプリケーション。

私と同じような他の質問を読んだことがありますが、SQLに少し慣れていないので、なぜこれが起こっているのか理解できません。

同様の質問:
- MySQL挿入ステートメントのデッドロック
- 2つの単純な挿入クエリのMySQL SQL InnoDBデッドロック

更新:

READ COMMITTEDを使用しても実際には問題が解決されないことがわかりました。デッドロックが発生している理由がまだわかりません。また、現在の診断方法よりも詳しい診断方法がわかりません。私は本番システムで引き続きデッドロックを取得しています。任意の助けいただければ幸いです。

8
Brian Sizemore

私はMySQLの専門家ではありませんが、ステートメントごとに異なる車両IDを挿入しているにもかかわらず、デッドロックログを見ると、ロックされるVehicleID非クラスタ化インデックスのデータページ全体(238326))が必要です

ときどきデッドロックが発生するということは、1ページ内にmultiple車両IDがあるため、2つの異なるプロセスが同じページのロックを必要とする可能性が少しあることを意味します。

アドバイスするのに最適なのはトランザクションをできるだけ小さくするです。

次のことができる方法がある場合、デッドロックの可能性を減らすのに役立ちます。

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

可能であれば、そのインデックスのFILLFACTORを95%に変更を試して、デッドロックが少なくなるかどうかをテストしてください。

より極端なテストは、挿入中にそのインデックスを削除するを完全に実行し、完了したらそれを再作成するを実行することです。

4
Oreo

MySQLは影響を受ける行をロックするだけでなく、影響を受けるインデックス行と、インデックス行間のギャップもロックします(説明 here )。主キーには常にインデックスが付けられており、更新でそれらを使用するので、複数のトランザクションがそれぞれ複数の行を更新しようとすると、重複するインデックスギャップロックが発生し、デッドロックが発生すると考えられます。

これを解決するために、トランザクションをできるだけ小さくするようにOreosのアドバイスをお勧めします。更新された行が互いに独立している場合は、行ごとに個別のトランザクションを使用する必要があります。

1
Flourid