MySql Gap Lock Deadlock на вставках

8

Я получаю Deadlocks от блокировок на столе при частой вставке из нескольких источников. Вот краткий обзор моих процессов.

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 и до сих пор не могу понять, почему это происходит.

Подобные вопросы:
- Deadlock на MySQL вставки статистики
- MySQL InnoDB Deadlock Для 2 простых запросов вставки

ОБНОВИТЬ:

Я обнаружил, что использование READ COMMITTEDфактически не решило проблему. Я до сих пор не выяснил, почему возникают тупики, и я действительно не знаю, как диагностировать дальше, чем в настоящее время. Я продолжаю получать тупики в моей производственной системе. Любая помощь будет оценена.

Брайан Сайзмор
источник
Не могли бы вы дать нам более подробную информацию, в частности: конфигурацию диска, количество пластин, характеристики производительности? Оперативки сколько? Процессор, количество и производительность? Количество транзакций в секунду, в минуту, в час и в день? Эти ставки меняются со временем? Как именно эти тупики влияют на производительность? Дайте нам вывод SHOW PROCESSLIST;. В большинстве случаев REPEATABLE READэто лучший уровень изоляции для большинства приложений, поэтому я не буду слишком беспокоиться о его использовании. Было ли заметное увеличение производительности, когда вы изменили его по умолчанию - REPEATABLE READ?
Верас
Как это может работать? У вас есть строки без кавычек VARCHARs.
Рик Джеймс
Где КОНЕЦ ЦИКЛА?
Рик Джеймс
@RickJames У меня нет строк без кавычек, идущих в VARCHARS, запросы работают, как и ожидалось, когда выполняются в 95% случаев. END LOOP обозначается вкладкой обратно на тот же уровень. Например, цикл начинается, я запускаю этот оператор вставки несколько раз, затем цикл заканчивается и транзакция заканчивается. Обратите внимание, что loopэто просто псевдокод для представления происходящего.
Брайан Сайзмор
@ Vérace Repeatable Read - значение по умолчанию для этой таблицы (с использованием механизма innodb). Я тестировал его изменения от repeatable readк read committedкоторой является более низкий уровнем изоляции , то повторяемость чтения, но , к сожалению , это не остановило тупик. Я знаю, что аппаратное обеспечение будет влиять на сервер (это экземпляр ec2, мне нужно было бы посмотреть особенности), но я не думаю, что эта информация была бы необходима, чтобы понять, почему возникают взаимоблокировки. Спорадическая природа этого также затрудняет захват результатов show processlist; когда возникает тупик.
Брайан Сайзмор

Ответы:

4

Я не эксперт по MySQL, но, судя по вашим журналам тупиковой ситуации, несмотря на то, что вы ВСТАВЛЯЕТЕ разные идентификаторы транспортных средств на оператор, для них требуется блокировка всей страницы данных (238326) VehicleIDнекластеризованного индекса .

Тот факт, что вы время от времени получаете взаимоблокировки, означает, что на одной странице у вас есть несколько идентификаторов транспортных средств, поэтому существует небольшая вероятность того, что 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% , и проверьте, не получите ли вы меньше взаимоблокировок.

Более экстремальное испытание было бы удалить этот индекс полностью, вставляя, а затем воссоздать его , когда сделано.

Oreo
источник
Есть ли у вас какие-либо идеи относительно того, почему вся страница будет заблокирована, а не только строки, которые я пытаюсь вставить?
Брайан Сайзмор
1
Кроме того, я собираюсь немного изменить свой код и сократить время транзакций. Я считаю, что вы правы, что это должно иметь существенное значение.
Брайан Сайзмор
Не уверен, как работают внутренние компоненты MySQL, но этот ответ объясняет это для MS SQL. Несколько хороших советов по MySQL в руководстве по MySQL .
Oreo
1
MySQL не обеспечивает контроль за fillfactor.
Рик Джеймс
2
После рефакторинга моего кода для постановки в очередь моих вставок и оператора обновления и выполнения их очень близко друг к другу, это решило мою проблему. Не только это, но я смог продолжать увеличивать это (примерно вдвое больше предыдущего количества параллельных процессов), и он все еще работает правильно. Спасибо, Орео!
Брайан Сайзмор
1

MySQL не только блокирует затронутую строку, но также затрагивает строку индекса и разрыв между строками индекса (как описано здесь ). Поскольку первичные ключи всегда индексируются, и вы используете их в своих обновлениях, я подозреваю, что несколько транзакций, пытающихся обновить несколько строк, приводят к перекрывающимся блокировкам пробела индекса, которые, в свою очередь, создают тупик.

Чтобы решить эту проблему, я также рекомендую совету Oreos сделать транзакцию как можно меньше. Если обновленные строки не зависят друг от друга, вы должны использовать отдельную транзакцию для каждой из них.

Flourid
источник