Исправление «Превышен таймаут ожидания блокировки; попробуйте перезапустить транзакцию для «зависшей» таблицы Mysql?

132

Из сценария я тысячи раз отправлял такой запрос в мою локальную базу данных:

update some_table set some_column = some_value

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

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

Lock wait timeout exceeded; try restarting transaction

Это таблица innodb, поэтому зависшая транзакция, вероятно, неявная. Как я могу исправить эту таблицу и удалить из нее зависшую транзакцию?

Том
источник
3
Что на выходе SHOW FULL PROCESSLIST?
Вольф
Он показывает только команду SHOW FULL PROCESSLIST и ничего больше. Это локальная база данных разработки. На нем ничего не работает. Я получил сообщение об ошибке «ожидание блокировки ...» в командной строке, когда я попытался удалить индекс оттуда.
Том
В этом случае вы, вероятно, создаете 2 отдельных соединения в разных транзакциях, которые должны ждать друг друга.
Вольф
После этого я не создавал никаких транзакций. Я убил сценарий, перезагрузил машину и вошел в систему из командной строки, чтобы осмотреться. Ничто другое не использовало базу данных, кроме клиента командной строки mysql, поэтому что-то должно было застрять в таблице.
Том
1
Связанный вопрос: как отладить время ожидания блокировки превышено?
Амир Али Акбари

Ответы:

143

У меня была аналогичная проблема, и я решил ее, проверив запущенные потоки. Чтобы увидеть запущенные потоки, используйте следующую команду в интерфейсе командной строки mysql:

SHOW PROCESSLIST;

Его также можно отправить из phpMyAdmin, если у вас нет доступа к интерфейсу командной строки mysql.
Это отобразит список потоков с соответствующими идентификаторами и временем выполнения, поэтому вы можете УБИТЬ потоки, выполнение которых занимает слишком много времени. В phpMyAdmin у вас будет кнопка для остановки потоков с помощью KILL, если вы используете интерфейс командной строки, просто используйте команду KILL, за которой следует идентификатор потока, как в следующем примере:

KILL 115;

Это завершит соединение для соответствующего потока.

Михай Крэицэ
источник
36
ОБРАТИТЕ ВНИМАНИЕ! Это было упомянуто кем-то в одном из многих потоков SO, касающихся этой проблемы: Иногда процесс, заблокировавший таблицу, отображается в списке процессов как спящий! Я рвал на себе волосы, пока не убил все нити, которые были открыты в рассматриваемой базе данных, спящие они или нет. Это, наконец, разблокировало таблицу и позволило запустить запрос на обновление. Комментатор упомянул что-то вроде «Иногда поток MySQL блокирует таблицу, а затем засыпает, ожидая чего-то, не связанного с MySQL».
Эрик Л.
(Я добавил свой собственный ответ, чтобы конкретизировать этот фрагмент мысли здесь , на связанный вопрос)
Эрик Л.
Это мне помогло. У меня было несколько спящих потоков, созданных функцией управления / запросов базы данных PHPStorm.
Ejaz
Большой! У меня был скрытый процесс с 5 часов, который я мог идентифицировать и убить.
Aldo Paradiso
2
это решение не больше, чем решение проблемы, когда заклеить инфицированную рану лентой. вы не решаете основную проблему.
user2914191
56

Вы можете проверить текущие транзакции с помощью

SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`

Ваша транзакция должна быть одной из первых, потому что она самая старая в списке. Теперь просто возьмите значение trx_mysql_thread_idи отправьте ему KILLкоманду:

KILL 1234;

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

nlsrchtr
источник
Возможно, вам потребуется использовать учетную запись root для запуска этого SQL, чтобы увидеть, какая транзакция фактически блокирует доступ к таблице
другим пользователям
40

Проверить статус InnoDB на наличие блокировок

SHOW ENGINE InnoDB STATUS;

Проверить открытые таблицы MySQL

SHOW OPEN TABLES WHERE In_use > 0;

Проверить ожидающие транзакции InnoDB

SELECT * FROM `information_schema`.`innodb_trx` ORDER BY `trx_started`; 

Проверить зависимость блокировки - что что блокирует

SELECT * FROM `information_schema`.`innodb_locks`;

Изучив приведенные выше результаты, вы сможете увидеть, что что блокирует.

Основная причина проблемы также может быть в вашем коде - пожалуйста, проверьте связанные функции, особенно на предмет аннотаций, если вы используете JPA, например Hibernate.

Например, как описано здесь , неправильное использование следующей аннотации может вызвать блокировки в базе данных:

@Transactional(propagation = Propagation.REQUIRES_NEW) 
martoncsukas
источник
4
Спасибо вам большое! Запуск SELECT * FROM information_schema.innodb_trx t JOIN information_schema.processlist p ON t.trx_mysql_thread_id = p.idвыявил виновника: поток блокировки пришел с моего IP-адреса ... Я забыл закрыть консоль отладки, которую я оставил в середине транзакции ...
Элиас Штреле
40

Это начало происходить со мной, когда размер моей базы данных вырос и я выполнял с ней много транзакций.

Правда в том, что, вероятно, есть способ оптимизировать либо ваши запросы, либо вашу БД, но попробуйте эти 2 запроса для исправления работы.

Запустите это:

SET GLOBAL innodb_lock_wait_timeout = 5000; 

А потом это:

SET innodb_lock_wait_timeout = 5000; 
Макс D
источник
2
Ссылка для ссылки: innodb_lock_wait_timeout
Culix
1
У меня очень загруженная база данных размером 11 ГБ. Это единственное, что у меня сработало, спасибо!
dongemus
Просто не забудьте изменить значения на предыдущие, если вы не хотите хранить их там навсегда.
Рикардо Мартинс
Это означает, что некоторые из моих пользователей будут работать медленнее, верно?
Арнольд Роа
9

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

Решение: закрыть соединение или setAutoCommit(true)(по вашему замыслу) снять блокировку.

user3388324
источник
7

Перезагрузите MySQL, все работает нормально.

НО будьте осторожны, если такой запрос завис, где-то есть проблема:

  • в вашем запросе (неуместный символ, декартово произведение, ...)
  • очень много записей для редактирования
  • сложные соединения или тесты (MD5, подстроки LIKE %...%и т. д.)
  • проблема структуры данных
  • модель внешнего ключа (блокировка цепи / петли)
  • неверно проиндексированные данные

Как сказал @syedrakib, это работает, но это не долгоживущее решение для производства.

Остерегайтесь: перезапуск может повлиять на ваши данные с несогласованным состоянием.

Кроме того, вы можете проверить, как MySQL обрабатывает ваш запрос, с помощью ключевого слова EXPLAIN и посмотреть, возможно ли что-то для ускорения запроса (индексы, сложные тесты, ...).

Benj
источник
СПАСИБО. вы не решили мою проблему напрямую, но я страдал от блокировки таблицы, и это было так плохо. Это единственное сообщение во всем Интернете, которое натолкнуло меня на мысль проверить внешние ключи. Итак, я узнал, что ключ первичного ключа был 11, а внешних ключей 10. Я не знаю, как это могло произойти и почему все работало раньше.
EscapeNetscape
@EscapeNetscape, пожалуйста, я рад, что этот небольшой ответ вам помог :)
Бендж
3

Перейти к процессам в mysql.

Итак, вы видите, что задача все еще работает.

Убейте конкретный процесс или дождитесь завершения процесса.

Шемер М. Али
источник
7
Это решает проблему. Но это не может быть решением для рабочего сервера LIVE ...... Как мы можем вывести эту обработку тупиков? Или как мы можем ИЗБЕЖАТЬ этого тупика?
Ракиб
1
ну, так уж случилось, что даже с ИДЕАЛЬНО правильно сформированными и ИДЕАЛЬНО экранированными запросами mysql, которые даже написаны не разработчиком, а интерфейсом активных записей фреймворка, проблемы с тайм-аутом ожидания блокировки все еще могут возникать. Поэтому я не пишу правильный запрос mysql - это фактор здесь.
Rakib
1

Я столкнулся с той же проблемой с заявлением «обновить». Мое решение заключалось в том, чтобы просто выполнить операции, доступные в phpMyAdmin для таблицы. Я оптимизировал, очистил и дефрагментировал таблицу (не в таком порядке). Мне не нужно отбрасывать таблицу и восстанавливать ее из резервной копии. :)

Мальчик науки
источник
1
Это могло бы решить проблему. Но необходимость делать это каждый раз, когда возникает такая ошибка, не может быть решением для рабочего сервера LIVE ...... Как мы можем вывести эту обработку тупиков? Или как мы можем ИЗБЕЖАТЬ этого тупика?
Ракиб
1

Я была такая же проблема. Я думаю, это была проблема с SQL. Вы можете просто принудительно закрыть процесс SQL из диспетчера задач. Если это не помогло, просто перезагрузите компьютер. Вам не нужно отбрасывать таблицу и перезагружать данные.

kriver
источник
Это решает проблему. Но это не может быть решением для рабочего сервера LIVE ...... Как мы можем вывести эту обработку тупиков? Или как мы можем ИЗБЕЖАТЬ этого тупика?
Ракиб
перезапустите Apache и его службы (или, по крайней мере, MySQL), перезагружать не нужно
Amjo
1

У меня возникла эта проблема при попытке удалить определенную группу записей (с использованием MS Access 2007 с подключением ODBC к MySQL на веб-сервере). Обычно я удаляю определенные записи из MySQL, а затем заменяю их обновленными (каскадное удаление нескольких связанных записей, это упрощает удаление всех связанных записей для удаления одной записи).

Я попытался выполнить операции, доступные в phpMyAdmin для таблицы (оптимизация, очистка и т. Д.), Но при попытке сброса я получал необходимое разрешение на ошибку RELOAD. Поскольку моя база данных находится на веб-сервере, мне не удалось перезапустить базу данных. Восстановление из резервной копии было невозможно.

Я попытался запустить запрос на удаление для этой группы записей при доступе cPanel mySQL в Интернете. Получено такое же сообщение об ошибке.

Мое решение: я использовал бесплатный браузер запросов MySQL от Sun (Oracle) (который я ранее установил на свой компьютер) и выполнил там запрос на удаление. Сработало сразу, проблема решена. Затем я снова смог выполнить эту функцию, используя сценарий Access, используя соединение ODBC Access с MySQL.

Майк Лейн
источник
-2

Починил это.

Убедитесь, что в запросе нет вставки несоответствующего типа данных. У меня была проблема, когда я пытался использовать "данные агента браузера пользователя" VARCHAR(255)и у меня возникла проблема с этой блокировкой, однако, когда я изменил ее, TEXT(255)она исправила ее.

Так что скорее всего это несоответствие типа данных.

Хирен Радж
источник
-151

Я решил проблему, отбросив таблицу и восстановив ее из резервной копии.

Том
источник
20
Между прочим, этот ответ удостоен чести быть принятым ответом SO с наименьшей оценкой за все время! 🏆
ashleedawg
1
Это также самый низкий результат в целом
Боб Керман,