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

267

Я запускаю следующую UPDATEинструкцию MySQL :

mysql> update customer set account_import_id = 1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

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

В таблице 406 733 строки.

Джейсон Светт
источник

Ответы:

211

Вы используете транзакцию; autocommit не отключает транзакции, он просто автоматически фиксирует их в конце оператора.

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

Вы можете увидеть более подробную информацию о событии, выпустив

SHOW ENGINE INNODB STATUS

после события (в sqlредакторе). Идеально делать это на тихой тестовой машине.

MarkR
источник
1
Есть ли способ сохранить вывод в файл? Я попытался ПОКАЗАТЬ СТАТУС ДВИГАТЕЛЯ INNODB \ G> innodb_stat.txt, но не работает.
Январь
14
Из командной строки: mysql [вставить учетные данные] -e "ПОКАЗАТЬ СТАТУС ДВИГАТЕЛЯ INNODB \ G"> innodb_stat.txt
VenerableAgents
если многие потоки (или процессы) mysql заняты, например, некоторые запросы требуют очень много времени, поэтому вам придется подождать, пока некоторые не processбудут работать. Если это так, вы можете получить эту ошибку. Я прав?
zhuguowei
6
Выполнение нескольких (2+) запросов UPDATE в одной строке во время одной транзакции также приведет к этой ошибке.
Окнелопер
Для тех, кто использует Python MySQL Connector, используйте connection.commit()коммит INSERTили UPDATEвы только что пробежались.
AER
334

Как принудительно разблокировать заблокированные таблицы в MySQL:

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

Это хакерское решение, и правильное решение - исправить приложение, которое вызвало блокировки. Однако, когда доллары на линии, быстрый удар заставит вещи двигаться снова.

1) Войдите в MySQL

mysql -u your_user -p

2) Давайте посмотрим список заблокированных таблиц

mysql> show open tables where in_use>0;

3) Давайте посмотрим список текущих процессов, один из них блокирует ваши таблицы

mysql> show processlist;

4) Убей один из этих процессов

mysql> kill <put_process_id_here>;
Эрик Лещинский
источник
14
Это опасно и взломано. Правильное решение - исправить ваше приложение.
Zenexer
71
Ерунда, это позволяет отменить ошибку и затем исправить приложение. Если бы я мог отдать этому парню 100 голосов за эту проблему, которую я должен был исправить СЕЙЧАС, я бы сделал это.
Lizardx
7
Я согласен с Lizardx. Это было очень полезное решение в ситуации, когда у меня не было права звонить ПОКАЗАТЬ СТАТУС ДВИГАТЕЛЯ INNODB
Travis Schneeberger
8
Насколько опасно убивать длительные запросы ? Вызывающий клиент просто получит ошибку.
Xeoncross
7
@EricLeschinski Я понимаю вашу точку зрения, но я должен спросить, почему в мире вы использовали бы небрежную базу данных, такую ​​как MySQL, в системе Life-Critical ?
Xeoncross
96
mysql> set innodb_lock_wait_timeout=100

Query OK, 0 rows affected (0.02 sec)

mysql> show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 100   |
+--------------------------+-------+

Теперь снова активируйте замок. У вас есть 100 секунд, чтобы выдать SHOW ENGINE INNODB STATUS\Gбазу данных и посмотреть, какая другая транзакция блокирует вашу.

Вейн
источник
8
Этот ответ не объясняет, почему спрашивающий получает свою ошибку. Не могли бы вы пояснить, почему помимо того, чтобы просто дать ответ?
сани
5
+1, хотя это не дает прямого ответа на вопрос, для меня это хорошая ссылка для обхода этой проблемы.
Йосеф Харуш
1
@ArtB dev.mysql.com/doc/innodb/1.1/en/… По сути, OP получает ошибку, потому что для таблицы была вызвана блокировка, и время, прошедшее до завершения транзакции, превысило lock_wait_timeoutзначение
фырье
70

Посмотрите, хорошо ли настроена ваша база данных. Особенно изоляция транзакций. Не стоит увеличивать переменную innodb_lock_wait_timeout.

Проверьте уровень изоляции транзакции базы данных в mysql cli:

mysql> SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation, @@session.transaction_isolation;
+-----------------------+-----------------+------------------------+
| @@GLOBAL.tx_isolation | @@tx_isolation  | @@session.tx_isolation |
+-----------------------+-----------------+------------------------+
| REPEATABLE-READ       | REPEATABLE-READ | REPEATABLE-READ        |
+-----------------------+-----------------+------------------------+
1 row in set (0.00 sec)

Вы можете получить улучшения, изменяя уровень изоляции, используя оракул, такой как READ COMMITTED, вместо REPEATABLE READ (InnoDB Defaults)

mysql> SET tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> SET GLOBAL tx_isolation = 'READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)

mysql> 

Также попробуйте использовать SELECT FOR UPDATE только в случае необходимости.

saisyukusanagi
источник
1
Это отличное решение для проблем блокировки.
благоухающий
3
Прекрасно работает для меня, версия my.cnf: [mysqld] транзакция-изоляция = READ-COMMITTED
Бенуа Готье,
Просто примечание: MySQL 8 переименовал tx_isolationпеременную в transaction_isolation.
ксоня
Однако нужно позаботиться о том, во что вы ввязываетесь, когда вы переходите с изоляции на чтение на READ COMMITTED. Вы можете получить грязные данные, которых вы можете избежать. Википедия Isoloation
Huggie
27

Ни одно из предложенных решений не сработало для меня, но это сработало.

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

SHOW PROCESSLIST;

Как только вы обнаружите процесс блокировки, найдите его idи запустите:

KILL {id};

Повторите ваш начальный запрос.

BassMHL
источник
Я случайно УБил все процессы, перечисленные в SHOW PROCESSLIST; Теперь я получаю 500 ошибок в phpmyadmin. Эта ошибка 500 связана с убийством этих процессов? Если да, как я могу перезапустить его.
Дашрат
Я вижу, что вы описываете, однако, процесс убийства не работает. Команда процесса «убита», но она остается в списке процессов.
Торстен
12

100% с тем, что сказал MarkR. autocommit делает каждый оператор транзакцией одного оператора.

SHOW ENGINE INNODB STATUSдолжен дать вам некоторые подсказки относительно причины тупика. Внимательно посмотрите на свой медленный журнал запросов, чтобы увидеть, что еще запрашивает таблицу, и попытайтесь удалить все, что делает полный просмотр таблицы. Блокировка на уровне строк работает хорошо, но не тогда, когда вы пытаетесь заблокировать все строки!

Джеймс С
источник
5

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

Джон Кейн
источник
3
возможно innodb_lock_wait_timeoutвmy.cnf
обязательство
1
Я установил в my.cnf: <br/> innodb_lock_wait_timeout = 120 <br/> По умолчанию 50 для MySQL 5.5. После этого изменения я не смог увидеть эту проблему в своих модульных тестах! Это произошло после переключения с proxool в пул tomcat jdbc. Вероятно, из-за большего времени транзакции с пулом Tomcat ?!
Champ
3

Количество строк невелико ... Создайте индекс для account_import_id, если это не первичный ключ.

CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);
гладиатор
источник
OMG ... это только спасло меня. Я по-королевски прикрутил производственную БД, сбросив индекс, и это исправило это. Спасибо вам.
Ник Готч
2

Если вы только что убили большой запрос, это займет время rollback. Если вы выполните другой запрос до того, как завершенный откат будет завершен, вы можете получить ошибку тайм-аута блокировки. Вот что случилось со мной. Решение было просто немного подождать.

Подробности:

Я выполнил запрос DELETE, чтобы удалить около 900 000 из примерно 1 миллиона строк.

Я запустил это по ошибке (удаляет только 10% строк): DELETE FROM table WHERE MOD(id,10) = 0

Вместо этого (удаляет 90% строк): DELETE FROM table WHERE MOD(id,10) != 0

Я хотел удалить 90% строк, а не 10%. Поэтому я убил процесс в командной строке MySQL, зная, что он откатит все строки, которые он удалил до сих пор.

Затем я сразу же выполнил правильную команду и lock timeout exceededвскоре получил ошибку. Я понял, что на самом деле блокировка может быть rollbackиз-за убитого запроса, все еще происходящего в фоновом режиме. Поэтому я подождал несколько секунд и снова запустил запрос.

Баттл Буткус
источник
1

Убедитесь, что таблицы базы данных используют механизм хранения InnoDB и уровень изоляции транзакций READ-COMMITTED.

Вы можете проверить это SELECT @@ GLOBAL.tx_isolation, @@ tx_isolation; на консоли MySQL.

Если он не установлен как READ-COMMITTED, вы должны установить его. Перед установкой убедитесь, что у вас есть привилегии SUPER в mysql.

Вы можете воспользоваться помощью http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html .

Установив это, я думаю, что ваша проблема будет решена.


Вы также можете проверить, не пытаетесь ли вы обновить это сразу в двух процессах. Пользователи (@tala) сталкивались с подобными сообщениями об ошибках в этом контексте, возможно, еще раз проверьте, что ...

Рави Чхатрала
источник
1

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

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

Чтобы соответствовать примеру OP, это должно сработать:

mysql> set autocommit=0;

mysql> update customer set account_import_id = 1; commit;

Не забудьте снова активировать, autocommitесли вы хотите оставить конфигурацию MySQL, как и раньше.

mysql> set autocommit=1;

Kamae
источник
0

Поздно к вечеринке (как обычно), однако, моя проблема заключалась в том, что я написал какой-то плохой SQL (будучи новичком), и у нескольких процессов была блокировка записи (записей) <- не уверен в правильном словесном выражении. Мне пришлось просто: SHOW PROCESSLISTа затем убить идентификаторы с помощьюKILL <id>

Смитти
источник
0

Подобные вещи случались со мной, когда я использовал выход из PHP-конструкции; в середине транзакции. Затем эта транзакция «зависает», и вам нужно убить процесс mysql (описанный выше с помощью processlist;)

TomoMiha
источник
0

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

LOCK TABLES `customer` WRITE;
update customer set account_import_id = 1;
UNLOCK TABLES;

Это, вероятно, не очень хорошая идея для нормального использования.

Для получения дополнительной информации см .: Справочное руководство по MySQL 8.0

Джефф Люет
источник
0

Я столкнулся с этим, имея 2 соединения Doctrine DBAL, одно из которых как нетранзакционные (для важных журналов), они предназначены для параллельной работы независимо друг от друга.

CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery()
)

Мои тесты интеграции были свернуты в транзакции для отката данных после самого теста.

beginTransaction()
CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery() // CONFLICT
)
rollBack()

Мое решение состояло в том, чтобы отключить транзакцию обертывания в этих тестах и ​​сбросить данные БД другим способом.

Фабиан Пиконе
источник
-4

Была такая же ошибка, хотя я обновлял только одну таблицу с одной записью, но после перезапуска mysql она была исправлена.

tubostic
источник