it-roy-ru.com

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

Я использую следующий оператор MySQL UPDATE:

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

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

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

192
Jason Swett

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

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

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

SHOW ENGINE INNODB STATUS

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

164
MarkR

КАК ПРИНЯТЬ РАЗБЛОКИРОВКУ для заблокированных таблиц в MySQL:

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

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

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>;
252
Eric Leschinski
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 в базу данных и посмотреть, какая другая транзакция блокирует вашу.

76
veen

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

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

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

Вы можете получить улучшения, изменяя уровень изоляции, используя Oracle вместо 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 только в случае необходимости.

62
saisyukusanagi

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

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

SHOW PROCESSLIST;

Найдя процесс блокировки, найдите его id и запустите: 

KILL {id};

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

12
BassMHL

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

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

12
James C

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

5
John Kane

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

CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);
1
gladiator

Убедитесь, что таблицы базы данных используют механизм хранения 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
Ravi Chhatrala

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

0
TomoMiha

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

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

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

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

0
Jeff Luyet

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

0
Smitty

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

CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery()
)

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

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

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

0
Fabian Picone

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

0
Buttle Butkus

Я пришел из 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;

0
Kamae