Предисловие

Deadlock found when trying to get lock; try restarting transaction
Страшно? Если да — предположу, что вы, как и я, не DBA. Может, и опыта борьбы с дедлоками нет (от слова «совсем»), а километровый лог вовсе наводит ужас, щекоча нервы всякими непонятными словами. А тут еще и задача какая-то суперважная: миграция с MyISAM на InnoDB или падение какого-то важного функционала в рамках более-менее нагруженного проекта. Да еще и, не дай Бог, на продакшн-сервере.

Raisins_Face--

Возможно, вы уже успели вскипятить себе мозги 🤯️ перечитанными статьями и документацией, но целостного понимания сложившейся ситуации все нет?

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

Я в таких случаях пытаюсь визуализировать происходящие процессы в спотыкающейся системе, но в случае с дедлоками столкнулся с трудностью — никакой GUI-утилиты для этого не нашел. Что ж, «стадион так стадион» — пришлось изобретать самому.

Небольшой инструмент, описанный ниже, родился в процессе моей борьбы с многочисленными дедлоками и помогает мне быстрее и проще вникнуть в причины и суть происходящего.

Итак, вот что нам нужно

  • СУБД MySQL/MariaDB;
  • реляционная БД на движке InnoDB;
  • эмулятор терминала, который может в одном окне сеткой вывести несколько отдельных сессий (табы, на мой взгляд, не очень-то подходят для удобного мониторинга, а так будет некое подобие GUI). Я использую Terminator — он позволяет видеть все и сразу, а также удобно всем этим управлять.

Для примера я использую Ubuntu, MariaDB под Docker’ом и FISH в качестве шелла. Имейте это ввиду, выполняя приведенные скрипты на своей машине.

Для воссоздания дедлока нам нужны конкурирующие транзакции, то есть мы в разных соединениях должны одновременно работать с одними и теми же данными.

Открываем в Terminator 2 сессии ( я назвал trx1 и trx2) и коннектимся в каждой к БД.

fish---home-prid_096

Теперь разберемся с тем, что хотим мониторить, чтобы видеть что происходит под капотом:

  • Текущие транзакции (transactions info)
    SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX
    Показывает полезную информацию о состоянии выполнения транзакций.
  • Активные локи (locks info)
    SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS
    Показывает, какая транзакция какой лок удерживает.

Мониторить эти данные будем путем оборачивания запросов в unix-утилитку watch. То есть получится примерно так:

watch -n1 "docker exec -it database mysql -u DB_USER -pDB_PASS -e 'SELECT * FROM information_schema.innodb_locks;'"

Добавляем в окне Terminator еще 2 сессии с этими запросами и получаем:

fish---home-prid_098

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

Ниже я прикрепил архив с конфигом Terminator’а и .desktop-файлом, чтоб вы могли удобно запускать его из меню.

Скачать архив.

P.S. Только не забудьте заменить DB_NAME, DB_USER, DB_PASS и абсолютные пути в файлах на свои.

P.P.S. Пара нюансов по прикрепленному конфигу:

  • Над описанными четырьмя окнами я прикреплял для себя еще одно со списком процессов СУБД, так как иногда использовал этот конфиг в системе, где могли параллельно выполняться другие задачи (например, запущенные по cron’у). И нужно было понимать, что моим экспериментам не мешает никакая другая сессия СУБД. Можете заменить запрос на, например, SHOW ENGINE INNODB STATUS;, чтоб видеть информацию о последнем случившемся дедлоке (если позволяет размер монитора), или вовсе удалить это окно из конфига.
  • При запуске Terminator’а с этим конфигом я объединил 2 нижних окна (trx1 и trx2) в группу Interactive transactions. Так что при запуске, когда курсор стоит в одном из этих окон — все что вы вводите, автоматически дублируется и во втором окне. Сделал я это, чтоб не писать START TRANSACTION; 2 раза 😊. Чтобы перестать дублировать контент — нажмите Alt+O, чтоб снова включить этот режим — Alt+G (обожаю Terminator 😍️).