В моих производственных журналах ошибок я иногда вижу:
SQLSTATE [HY000]: общая ошибка: 1205 Превышено время ожидания блокировки; попробуйте перезапустить транзакцию
Я знаю, какой запрос пытается получить доступ к базе данных в данный момент, но есть ли способ узнать, какой запрос был заблокирован в тот момент?
Ответы:
То, что выдает это, является словом транзакция . Из заявления видно, что запрос пытался изменить хотя бы одну строку в одной или нескольких таблицах InnoDB.
Поскольку вы знаете запрос, все таблицы, к которым осуществляется доступ, являются кандидатами на то, чтобы быть виновником.
Оттуда вы должны быть в состоянии бежать
SHOW ENGINE INNODB STATUS\G
Вы должны быть в состоянии увидеть затронутые таблицы
Вы получаете все виды дополнительной информации о блокировке и мьютексе.
Вот образец от одного из моих клиентов:
Вам следует рассмотреть возможность увеличения значения тайм-аута ожидания блокировки для InnoDB, установив innodb_lock_wait_timeout , по умолчанию 50 секунд
Вы можете установить его на более высокое значение
/etc/my.cnf
постоянно с этой линиейи перезапустите MySQL. Если вы не можете перезапустить MySQL в это время, запустите это:
Вы также можете просто установить его на время вашей сессии
с последующим вашим запросом
источник
innodb_lock_wait_timeout
переменная может быть установлена только при запуске сервера. Для плагина InnoDB он может быть установлен при запуске или изменен во время выполнения и имеет как глобальные значения, так и значения сеанса.SQL Error (1064): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\G' at line 1
SET GLOBAL innodb_lock_wait_timeout = 120;
снова запускаться . Если/etc/my.cnf
есть опция,innodb_lock_wait_timeout
устанавливается для вас. Не у всех есть привилегия SUPER, чтобы изменить это глобально для всех остальных ( dev.mysql.com/doc/refman/5.6/en/… )Как кто-то упомянул в одной из многих SO-тем, касающихся этой проблемы: иногда процесс, который заблокировал таблицу, отображается как спящий в списке процессов! Я вырывал свои волосы, пока не убил все спящие потоки, которые были открыты в рассматриваемой базе данных (ни один не был активен в то время). Это, наконец, разблокировало таблицу и позволило выполнить запрос на обновление.
Комментатор сказал что-то вроде: «Иногда поток MySQL блокирует таблицу, а затем спит, ожидая, когда произойдет нечто, не связанное с MySQL».
После повторного просмотра
show engine innodb status
журнала (после того, как я разыскал клиента, ответственного за блокировку), я заметил, что рассматриваемый застрявший поток был указан в самом низу списка транзакций, под активными запросами, которые собирались вызвать ошибку из-за замороженного замка:(не уверен, что сообщение «Просмотр чтения Trx» связано с заблокированной блокировкой, но в отличие от других активных транзакций, эта не отображается с выполненным запросом и вместо этого утверждает, что транзакция «очищается», но имеет несколько рядные замки)
Мораль этой истории заключается в том, что транзакция может быть активной, даже если поток спит.
источник
show processlist;
показать исчерпывающий список процессов, выполняемых в настоящее время, что приятно, потому что это сжатая версияshow engine innodb status\g
. Кроме того, если ваша база данных находится на экземпляре Amazon RDS, вы можете использовать ееCALL mysql.rds_kill(<thread_id>);
для уничтожения потоков. Я думаю, что у него более высокие разрешения, потому что он позволил мне убить больше процессов, чем простоkill <thread_id>;
- обратите внимание, что они должны выполняться в MySQL CLIИз-за популярности MySQL неудивительно, что превышено время ожидания блокировки; попробуйте перезапустить исключение транзакции получает столько внимания на SO.
Чем больше у вас разногласий, тем больше вероятность возникновения взаимоблокировок, которые механизм БД разрешит путем тайм-аута одной из взаимоблокированных транзакций. Кроме того, долгосрочные транзакции, которые изменили (например,
UPDATE
илиDELETE
) большое количество записей (которые принимают блокировки, чтобы избежать аномалий грязной записи, как объяснено в книге High-Performance Java Persistence ), с большей вероятностью вызывают конфликты с другими транзакциями.Хотя InnoDB MVCC, вы все равно можете запросить явные блокировки, используя
FOR UPDATE
предложение . Однако, в отличие от других популярных БД (Oracle, MSSQL, PostgreSQL, DB2), MySQL используетREPEATABLE_READ
уровень изоляции по умолчанию .Теперь полученные вами блокировки (либо путем изменения строк, либо с помощью явной блокировки) сохраняются на время текущей выполняемой транзакции. Если вы хотите получить хорошее объяснение различий между блокировками
REPEATABLE_READ
иREAD COMMITTED
в отношении блокировки, прочитайте эту статью о Percona .Следовательно: чем более ограничивающий уровень изоляции (
REPEATABLE_READ
,SERIALIZABLE
), тем больше вероятность тупика. Это не проблема "per se", это компромисс.Вы можете получить очень хорошие результаты
READ_COMMITED
, поскольку вам нужно предотвращать потерю обновлений на уровне приложений при использовании логических транзакций, которые охватывают несколько HTTP-запросов. В оптимистической блокировках цель подхода потеряла обновление , которые могут произойти , даже если вы используетеSERIALIZABLE
уровень изоляции при одновременном снижении раздора блокировки, позволяя использоватьREAD_COMMITED
.источник
Для записи, исключение тайм-аута ожидания блокировки также происходит, когда есть тупик, и MySQL не может обнаружить его, так что это просто время ожидания. Другой причиной может быть чрезвычайно длительный запрос, который проще решить / исправить, однако я не буду описывать этот случай здесь.
MySQL обычно может справиться с взаимоблокировками, если они построены «правильно» в течение двух транзакций. Затем MySQL просто убивает / откатывает одну транзакцию, которая владеет меньшим количеством блокировок (менее важно, так как это повлияет на меньшее количество строк), и позволяет другой завершиться.
Теперь предположим, что есть два процесса A и B и 3 транзакции:
Это очень неудачная установка, потому что MySQL не может видеть, что есть тупик (охватываемый в 3 транзакциях). Так что MySQL делает ... ничего! Он просто ждет, так как не знает, что делать. Он ожидает, пока первая полученная блокировка не превысит время ожидания (Процесс A, транзакция 1: блокировки X), затем разблокирует блокировку X, которая разблокирует транзакцию 2 и т. Д.
Задача состоит в том, чтобы выяснить, что (какой запрос) вызывает первую блокировку (Lock X). Вы сможете легко увидеть (
show engine innodb status
), что транзакция 3 ожидает транзакцию 2, но вы не увидите, какую транзакцию ожидает транзакция 2 (транзакция 1). MySQL не будет печатать какие-либо блокировки или запросы, связанные с транзакцией 1. Единственным указанием будет то, что в самом низу списка транзакций (show engine innodb status
распечатки) вы увидите, что транзакция 1, по-видимому, ничего не делает (но на самом деле ожидает транзакции 3, чтобы финиш).Методика определения того, какой SQL-запрос вызывает блокировку (Lock X) для данной транзакции, которая ожидает, описана здесь
Tracking MySQL query history in long running transactions
Если вам интересно, что за процесс и транзакция именно в примере. Процесс является процессом PHP. Транзакция - это транзакция, определенная innodb-trx-table . В моем случае у меня было два PHP-процесса, в каждом из которых я запускал транзакцию вручную. Интересно то, что, хотя я и начал одну транзакцию в процессе, MySQL фактически использовал две отдельные транзакции (я понятия не имею, почему, может быть, некоторые разработчики MySQL могут это объяснить).
MySQL внутренне управляет своими собственными транзакциями и решила (в моем случае) использовать две транзакции для обработки всех запросов SQL, поступающих от процесса PHP (Процесс A). Утверждение о том, что транзакция 1 ожидает завершения транзакции 3, является внутренней вещью MySQL. MySQL «знал», что транзакция 1 и транзакция 3 фактически были созданы как часть одного запроса «транзакции» (из процесса A). Теперь вся «транзакция» была заблокирована, потому что транзакция 3 (часть «транзакции») была заблокирована. Поскольку «транзакция» не смогла завершить транзакцию 1 (также часть «транзакции») была помечена как не завершенная. Это то, что я имел в виду под «Транзакцией 1, ожидающей завершения Транзакции 3».
источник
Большая проблема с этим исключением состоит в том, что его обычно невозможно воспроизвести в тестовой среде, и мы не собираемся запускать состояние движка innodb, когда это происходит на prod. Поэтому в одном из проектов я поместил приведенный ниже код в блок catch для этого исключения. Это помогло мне поймать состояние двигателя, когда произошло исключение. Это очень помогло.
источник
Взгляните на справочную страницу
pt-deadlock-logger
утилиты :Он извлекает информацию из
engine innodb status
упомянутого выше, а также может быть использован для созданияdaemon
каждые 30 секунд.источник
Экстраполируя ответ Роландо выше, именно они блокируют ваш запрос:
Если вам нужно выполнить ваш запрос и не можете дождаться запуска других, убейте их, используя идентификатор потока MySQL:
(изнутри mysql, а не оболочка, очевидно)
Вы должны найти идентификаторы потоков из:
команда и выяснить, какой из них блокирует базу данных.
источник
5341773
? Я не вижу, что отличает это одно от других.Вот что мне в конечном итоге пришлось сделать, чтобы выяснить, какой «другой запрос» вызвал проблему тайм-аута блокировки. В коде приложения мы отслеживаем все ожидающие обращения к базе данных в отдельном потоке, посвященном этой задаче. Если какой-либо вызов БД занимает больше N секунд (для нас это 30 секунд), мы регистрируем:
С учетом вышеизложенного мы смогли точно определить параллельные запросы, которые блокировали строки, вызывая взаимоблокировку. В моем случае это были операторы, подобные
INSERT ... SELECT
которым в отличие от простых команд SELECT блокируют основные строки. Затем вы можете реорганизовать код или использовать другую изоляцию транзакции, например, чтение без фиксации.Удачи!
источник
Ты можешь использовать:
который перечислит все соединения в MySQL и текущее состояние соединения, а также выполняемый запрос. Есть также более короткий вариант,
show processlist;
который отображает усеченный запрос, а также статистику соединения.источник
Если вы используете JDBC, то у вас есть опция
includeInnodbStatusInDeadlockExceptions = true
https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html
источник
Активируйте MySQL general.log (интенсивное использование диска) и используйте mysql_analyse_general_log.pl для извлечения длительных транзакций, например с:
Отключите general.log после этого.
источник