Obtendo “O tempo limite de espera para bloqueio foi excedido; tente reiniciar a transação "mesmo que eu não esteja usando uma transação

267

Estou executando a seguinte UPDATEinstrução MySQL :

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

Como não estou usando uma transação, por que estaria recebendo esse erro? Eu até tentei reiniciar meu servidor MySQL e não ajudou.

A tabela possui 406.733 linhas.

Jason Swett
fonte

Respostas:

211

Você está usando uma transação; A confirmação automática não desativa as transações, apenas as faz confirmar automaticamente no final do extrato.

O que está acontecendo é que algum outro encadeamento está mantendo um bloqueio de registro em algum registro (você está atualizando todos os registros da tabela!) Por muito tempo, e seu encadeamento está atingindo o tempo limite.

Você pode ver mais detalhes do evento emitindo um

SHOW ENGINE INNODB STATUS

após o evento (no sqleditor). Idealmente, faça isso em uma máquina de teste silenciosa.

MarkR
fonte
1
Existe uma maneira de salvar a saída em um arquivo? Eu tentei SHOW ENGINE INNODB STATUS \ G> innodb_stat.txt, mas não funciona.
Yantaq
14
Da linha de comando: mysql [inserir credenciais] -e "SHOW ENGINE INNODB STATUS \ G"> innodb_stat.txt
VenerableAgents
se muitos threads (ou processos) do mysql estão ocupados, por exemplo, algumas consultas precisam de muito tempo, então você deve esperar algumas processpara ficar ociosas. Nesse caso, você pode obter esse erro. Estou certo?
precisa saber é o seguinte
6
A execução de várias consultas UPDATE (2 ou mais) na mesma linha durante uma única transação também causará esse erro.
Okneloper
Para aqueles que usam o Python MySQL Connector, use o connection.commit()commit INSERTou UPDATEvocê acabou de passar.
AER
334

Como forçar o desbloqueio de tabelas bloqueadas no MySQL:

Quebrar bloqueios como esse pode fazer com que a atomicidade no banco de dados não seja imposta nas instruções sql que causaram o bloqueio.

Isso é uma tolice, e a solução adequada é corrigir o aplicativo que causou os bloqueios. No entanto, quando os dólares estão em jogo, um chute rápido fará as coisas se moverem novamente.

1) Digite MySQL

mysql -u your_user -p

2) Vamos ver a lista de tabelas bloqueadas

mysql> show open tables where in_use>0;

3) Vamos ver a lista dos processos atuais, um deles está bloqueando sua (s) tabela (s)

mysql> show processlist;

4) Mate um desses processos

mysql> kill <put_process_id_here>;
Eric Leschinski
fonte
14
Isso é perigoso e brincalhão. Uma solução adequada é consertar seu aplicativo.
Zenexer
71
Bobagem, isso permite desfazer uma confusão e, em seguida, corrigir o aplicativo. Se eu pudesse dar 100 votos a esse cara que eu tive que corrigir AGORA, eu daria.
Lizardx 26/05
7
Eu concordo com o Lizardx. Esta foi uma solução muito útil na situação que eu não tive o privilégio de chamar Show de INNODB STATUS
Travis Schneeberger
8
Como é perigoso matar uma consulta de longa duração dessa maneira ? A chamada do cliente receberá apenas um erro.
Xeoncross #
7
@EricLeschinski Entendi seu ponto de vista, mas tenho que perguntar por que diabos você usaria um banco de dados desleixado como o MySQL em um sistema crítico de vida ?
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   |
+--------------------------+-------+

Agora, ative a trava novamente. Você tem 100 segundos para emitir um SHOW ENGINE INNODB STATUS\Gno banco de dados e ver qual outra transação está bloqueando a sua.

veia
fonte
8
Esta resposta não explica por que o solicitante está recebendo o erro. Você poderia explicar por que além de apenas dar a resposta?
Trenó
5
+1, embora isso não responda diretamente à pergunta, para mim é uma boa referência para solucionar esse problema.
Jossef Harush
1
@ArtB dev.mysql.com/doc/innodb/1.1/en/… Em essência, o OP está recebendo o erro porque um bloqueio foi chamado na tabela e o tempo decorrido antes do término da transação excedeu o lock_wait_timeoutvalor
fyrye
70

Veja se o seu banco de dados está ajustado. Especialmente o isolamento de transações. Não é uma boa ideia aumentar a variável innodb_lock_wait_timeout.

Verifique o nível de isolamento da transação do banco de dados no 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)

Você pode obter melhorias alterando o nível de isolamento, use o oracle como READ COMMITTED e REPEATABLE READ (padrões do InnoDB)

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> 

Tente também usar SELECT FOR UPDATE apenas se necessário.

saisyukusanagi
fonte
1
Esta é uma ótima solução para problemas de bloqueio.
redolent
3
Funciona muito bem para mim, a versão my.cnf é [mysqld] transação-isolamento = READ-COMMITTED
Benoit Gauthier
Apenas uma observação: o MySQL 8 renomeou a tx_isolationvariável para transaction_isolation.
xonya
No entanto, é preciso tomar cuidado para saber em que você está se metendo quando você muda de isolamento de leitura para READ COMMITTED. Você pode acabar com dados sujos que pode querer evitar. Wikipedia Isoloation
huggie
27

Nenhuma das soluções sugeridas funcionou para mim, mas isso funcionou.

Algo está bloqueando a execução da consulta. Provavelmente outra consulta atualizando, inserindo ou excluindo de uma das tabelas em sua consulta. Você precisa descobrir o que é isso:

SHOW PROCESSLIST;

Depois de localizar o processo de bloqueio, encontre-o ide execute:

KILL {id};

Execute novamente sua consulta inicial.

BassMHL
fonte
Matei acidentalmente todos os processos listados com SHOW PROCESSLIST; Agora estou recebendo 500 erros no phpmyadmin. Esse erro 500 está relacionado a matar esses processos? Se sim, como posso reiniciá-lo.
Dashrath
Eu posso ver o que você descreve, no entanto, matar o processo não funciona. O comando do processo é "eliminado", mas permanece na lista de processos.
Torsten
12

100% com o que o MarkR disse. o autocommit faz de cada instrução uma transação de uma instrução.

SHOW ENGINE INNODB STATUSdeve lhe dar algumas pistas sobre o motivo do impasse. Dê uma boa olhada no seu log de consultas lentas também para ver o que mais está consultando a tabela e tente remover qualquer coisa que esteja executando uma verificação de tabela completa. O bloqueio no nível da linha funciona bem, mas não quando você está tentando bloquear todas as linhas!

James C
fonte
5

Você pode atualizar qualquer outro registro nessa tabela ou essa tabela é muito usada? O que estou pensando é que, enquanto está tentando adquirir um bloqueio, ele precisa atualizar esse registro, o tempo limite definido expirou. Você pode aumentar o tempo que pode ajudar.

John Kane
fonte
3
talvez innodb_lock_wait_timeoutemmy.cnf
obrigado 29/04
1
Eu configurei no my.cnf: <br/> innodb_lock_wait_timeout = 120 <br/> O padrão é 50 para o mysql 5.5. Após essa alteração, não consegui ver esse problema nos meus testes de unidade! Isso aconteceu após a mudança do proxool para o pool tomcat jdbc. Provavelmente devido a mais tempo de transação com o pool do tomcat ?!
Champ
3

O número de linhas não é enorme ... Crie um índice em account_import_id, se não for a chave primária.

CREATE INDEX idx_customer_account_import_id ON customer (account_import_id);
gladiador
fonte
OMG ... isso me salvou. Realmente estraguei um banco de dados de produção soltando um índice e isso foi corrigido. Te agradece.
Nick Gotch
2

Se você acabou de matar uma grande consulta, isso levará tempo rollback. Se você emitir outra consulta antes que a consulta finalizada seja revertida, poderá receber um erro de tempo limite de bloqueio. Foi o que aconteceu comigo. A solução foi apenas esperar um pouco.

Detalhes:

Eu havia emitido uma consulta DELETE para remover cerca de 900.000 de cerca de 1 milhão de linhas.

Fiz isso por engano (remove apenas 10% das linhas): DELETE FROM table WHERE MOD(id,10) = 0

Em vez disso (remove 90% das linhas): DELETE FROM table WHERE MOD(id,10) != 0

Eu queria remover 90% das linhas, não 10%. Então, eu matei o processo na linha de comando do MySQL, sabendo que ele reverteria todas as linhas que havia excluído até agora.

Em seguida, executei o comando correto imediatamente e recebi um lock timeout exceedederro logo depois. Percebi que o bloqueio pode realmente ser o rollbackda consulta morta ainda acontecendo no fundo. Então, esperei alguns segundos e refiz a consulta.

Buttle Butkus
fonte
1

Verifique se as tabelas do banco de dados estão usando o mecanismo de armazenamento InnoDB e o nível de isolamento de transação READ-COMMITTED.

Você pode verificá-lo por SELECT @@ GLOBAL.tx_isolation, @@ tx_isolation; no console do mysql.

Se não estiver definido para READ-COMMITTED, você deverá configurá-lo. Antes de configurá-lo, verifique se você possui privilégios SUPER no mysql.

Você pode obter ajuda em http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html .

Ao definir isso, acho que seu problema será resolvido.


Você também pode verificar se não está tentando atualizar isso em dois processos ao mesmo tempo. Os usuários (@tala) encontraram mensagens de erro semelhantes nesse contexto, talvez verifique duas vezes ...

Ravi Chhatrala
fonte
1

Eu vim do Google e só queria adicionar a solução que funcionou para mim. Meu problema era que eu estava tentando excluir registros de uma tabela enorme com muito FK em cascata, e obtive o mesmo erro do OP.

Desativei o autocommite, em seguida, funcionou apenas adicionando COMMITno final da sentença SQL. Tanto quanto eu entendi isso libera o buffer pouco a pouco, em vez de esperar no final do comando.

Para manter o exemplo do OP, isso deveria ter funcionado:

mysql> set autocommit=0;

mysql> update customer set account_import_id = 1; commit;

Não se esqueça de reativar autocommitnovamente se quiser deixar a configuração do MySQL como antes.

mysql> set autocommit=1;

Kamae
fonte
0

Tarde para a festa (como de costume), no entanto, meu problema foi o fato de eu ter escrito SQL incorreto (sendo iniciante) e vários processos terem bloqueado o (s) registro (s) <- não tenho certeza da verborragia apropriada. Acabei tendo que apenas: SHOW PROCESSLISTe depois matar os IDs usandoKILL <id>

Smitty
fonte
0

Esse tipo de coisa aconteceu comigo quando eu estava usando a saída de construção da linguagem php; no meio da transação. Então esta transação "trava" e você precisa matar o processo mysql (descrito acima com processlist;)

TomoMiha
fonte
0

Na minha instância, eu estava executando uma consulta anormal para corrigir dados. Se você bloquear as tabelas em sua consulta, não precisará lidar com o tempo limite de bloqueio:

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

Provavelmente, essa não é uma boa ideia para uso normal.

Para mais informações, consulte: Manual de Referência do MySQL 8.0

Jeff Luyet
fonte
0

Eu me deparei com isso com duas conexões DBAL do Doctrine, uma delas como não transacional (para logs importantes), e elas devem ser executadas em paralelo, não dependendo uma da outra.

CodeExecution(
    TransactionConnectionQuery()
    TransactionlessConnectionQuery()
)

Meus testes de integração foram agrupados em transações para reversão de dados após muito teste.

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

Minha solução foi desativar a transação de empacotamento nesses testes e redefinir os dados do banco de dados de outra maneira.

Fabian Picone
fonte
-4

Tive esse mesmo erro, embora eu estivesse atualizando apenas uma tabela com uma entrada, mas depois de reiniciar o mysql, ele foi resolvido.

tubostic
fonte