插入时MySql Gap Lock死锁


8

当从多个源频繁插入到表中时,我从间隙锁获取死锁。这是我的过程的概述。

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。我真的很困惑为什么我会遇到死锁。

我已经通过使用隔离级别临时解决了该问题READ COMMITTED,但是我读到这需要对复制进行更改,因为您必须进行行级复制。

我在这里阅读了其他与我的问题类似的问题,但是我对SQL还是有些陌生,仍然无法理解为什么会这样。

类似的问题:
- 死锁在MySQL插入statments
- MySQL的InnoDB的僵局2简单的插入查询

更新:

我发现使用READ COMMITTED并没有真正解决问题。我仍然没有弄清楚为什么会发生死锁,而且我真的不知道如何比现在更进一步诊断。我继续在生产系统中遇到僵局。任何帮助,将不胜感激。


您能否给我们提供更多详细信息-特别是:磁盘配置,磁盘数量,性能特征?RAM,多少钱?CPU,数量和性能?每秒,每分钟,每小时和每天的交易数量?这些费率会随着时间变化吗?这些僵局到底如何影响性能?给我们输出SHOW PROCESSLIST;。在大多数情况下,它REPEATABLE READ是大多数应用程序的最佳隔离级别,因此我不会太在意使用它。从默认值-更改时,性能是否显着提高REPEATABLE READ
Vérace

那怎么行?您已将未加引号的字符串放入VARCHARs
瑞克·詹姆斯

END LOOP在哪里?
瑞克·詹姆斯

@RickJames我没有将未加引号的字符串放入VARCHARS中,运行95%的时间时查询按预期工作。END LOOP由跳回到相同级别表示。例如,循环开始,我多次运行该insert语句,然后循环结束并且事务结束。注意,loop只是伪代码来表示正在发生的事情。
Brian Sizemore

该表的默认值是@VéraceRepeatable Read(使用innodb引擎)。我实际上测试了将其从更改为较低的隔离级别,然后repeatable readread committed其更改为可重复读取的方法,但不幸的是,它没有阻止死锁。我知道硬件会影响服务器(它是一个ec2实例,我必须查看具体细节),但是我认为了解死锁的原因并不需要该信息。零星的特性也使得很难捕获show processlist的输出。当发生死锁时。
Brian Sizemore

Answers:


4

我不是MySQL专家,但是从您的Deadlock日志来看,即使您在每个语句中插入不同的车辆ID,也需要锁定非聚集索引整个数据页(238326)VehicleID

事实上,您有时会陷入僵局,这意味着在1页内您有多个车辆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

如果可以,请尝试将该索引的填充因子更改为95%,然后进行测试以查看是否得到较少的死锁。

一个更极端的测试是在插入时完全删除该索引,然后在完成后重新创建它


您对为什么整个页面将被锁定而仅是我要插入的行有任何见解吗?
Brian Sizemore

1
另外,我将稍微重构代码并减少事务处理时间。我相信您是对的,它应该会产生重大的影响。
Brian Sizemore

不确定MySQL内部如何工作,但是此答案针对MS SQL进行了解释。MySQL手册中也有一些不错的MySQL技巧。
奥利奥

1
MySQL无法控制fillfactor。
瑞克·詹姆斯

2
重构我的代码以使我的插入内容和update语句排队并非常紧密地运行它们之后,它解决了我的问题。不仅如此,我还能够继续扩大规模(大约是以前并行进程数量的两倍),并且仍然可以正常运行。谢谢奥利奥!
Brian Sizemore

1

MySQL的不仅锁定了受影响的行,而且受影响的索引行和索引行之间的差距(如描述在这里)。由于主键始终处于索引状态,并且您在更新中使用它们,所以我怀疑尝试更新多个行的多个事务每个都会导致重叠的索引间隙锁,从而导致死锁。

为了解决这个问题,我还建议Oreos建议尽可能减少交易。如果更新的行彼此独立,则应为每个行使用单独的事务。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.