Nos meus logs de erro de produção, ocasionalmente vejo:
SQLSTATE [HY000]: Erro geral: 1205 Tempo limite de espera de bloqueio excedido; tente reiniciar a transação
Eu sei qual consulta está tentando acessar o banco de dados naquele momento, mas existe uma maneira de descobrir qual consulta teve o bloqueio naquele momento preciso?
Respostas:
O que revela isso é a palavra transação . É evidente pela declaração que a consulta estava tentando alterar pelo menos uma linha em uma ou mais tabelas do InnoDB.
Como você conhece a consulta, todas as tabelas que estão sendo acessadas são candidatas a culpadas.
A partir daí, você poderá executar
SHOW ENGINE INNODB STATUS\G
Você deve poder ver as tabelas afetadas
Você obtém todos os tipos de informações adicionais sobre bloqueio e mutex.
Aqui está uma amostra de um dos meus clientes:
Você deve considerar aumentar o valor do tempo limite de espera de bloqueio para o InnoDB configurando innodb_lock_wait_timeout , o padrão é 50 segundos
Você pode configurá-lo para um valor mais alto
/etc/my.cnf
permanentemente com esta linhae reinicie o mysql. Se você não pode reiniciar o mysql neste momento, execute o seguinte:
Você também pode configurá-lo para a duração da sua sessão
seguido pela sua consulta
fonte
innodb_lock_wait_timeout
variável pode ser definida apenas na inicialização do servidor. Para o InnoDB Plugin, ele pode ser definido na inicialização ou alterado no tempo de execução e possui valores globais e de sessão.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;
novamente. Se/etc/my.cnf
tiver a opção,innodb_lock_wait_timeout
está definido para você. Nem todo mundo tem o privilégio SUPER de alterá-lo globalmente para todos os outros ( dev.mysql.com/doc/refman/5.6/en/… ) #Como alguém mencionado em um dos muitos threads SO referentes a esse problema: Às vezes, o processo que bloqueou a tabela aparece como adormecido na lista de processos! Eu estava arrancando meus cabelos até matar todos os fios adormecidos que estavam abertos no banco de dados em questão (nenhum estava ativo no momento). Isso finalmente desbloqueou a tabela e deixou a consulta de atualização em execução.
O comentarista disse algo semelhante a "Às vezes, um thread do MySQL bloqueia uma tabela e dorme enquanto espera que algo não relacionado ao MySQL aconteça".
Após revisar novamente o
show engine innodb status
log (depois de rastrear o cliente responsável pelo bloqueio), notei que o encadeamento preso em questão estava listado na parte inferior da lista de transações, abaixo das consultas ativas que estavam prestes a ocorrer um erro devido ao bloqueio congelado:(não tem certeza se a mensagem "Trx read view" está relacionada ao bloqueio congelado, mas diferente das outras transações ativas, essa não aparece com a consulta emitida e afirma que a transação está "limpando", mas tem várias bloqueios de linha)
A moral da história é que uma transação pode estar ativa mesmo que o segmento esteja inativo.
fonte
show processlist;
para mostrar uma lista exaustiva dos processos atualmente em execução, o que é bom porque é uma versão condensada doshow engine innodb status\g
. Além disso, se o seu banco de dados estiver em uma instância do Amazon RDS, você poderá usarCALL mysql.rds_kill(<thread_id>);
para matar threads. Tem permissões superiores eu acho, porque me permitiu matar mais processos do que simpleskill <thread_id>;
- nota estes devem ser executados dentro MySQL CLIDevido à popularidade do MySQL, não é de admirar que o tempo de espera do bloqueio seja excedido; tente reiniciar a exceção da transação recebe muita atenção no SO.
Quanto mais contenção você tiver, maior a chance de conflitos, que um mecanismo de banco de dados resolverá ao atingir o tempo limite de uma das transações em conflito. Além disso, transações de longa execução que modificaram (por exemplo,
UPDATE
ouDELETE
) um grande número de entradas (que bloqueiam para evitar anomalias de gravação suja, conforme explicado no livro Java Persistence de alto desempenho ) têm mais probabilidade de gerar conflitos com outras transações.Embora InnoDB MVCC, você ainda pode solicitar bloqueios explícitos usando a
FOR UPDATE
cláusula . Entretanto, diferentemente de outros bancos de dados populares (Oracle, MSSQL, PostgreSQL, DB2), o MySQL usaREPEATABLE_READ
como o nível de isolamento padrão .Agora, os bloqueios que você adquiriu (modificando linhas ou usando bloqueio explícito) são mantidos durante a transação em execução no momento. Se você deseja uma boa explicação da diferença entre
REPEATABLE_READ
eREAD COMMITTED
no que diz respeito ao bloqueio, leia este artigo da Percona .Portanto: quanto mais restritivo o nível de isolamento (
REPEATABLE_READ
,SERIALIZABLE
) , maior a chance de conflito. Este não é um problema "per se", é uma troca.Você pode obter resultados muito bons
READ_COMMITED
, pois precisa de prevenção de atualizações perdidas no nível do aplicativo ao usar transações lógicas que abrangem várias solicitações HTTP. A abordagem de bloqueio otimista visa as atualizações perdidas que podem ocorrer mesmo se você usar oSERIALIZABLE
nível de isolamento e reduzir a contenção de bloqueio, permitindo o usoREAD_COMMITED
.fonte
Para o registro, a exceção de tempo limite de espera de bloqueio acontece também quando há um impasse e o MySQL não pode detectá-lo, então o tempo limite é excedido. Outro motivo pode ser uma consulta de execução extremamente longa, que é mais fácil de resolver / reparar, no entanto, e não descreverei esse caso aqui.
O MySQL geralmente é capaz de lidar com conflitos, se eles forem construídos "adequadamente" em duas transações. O MySQL simplesmente mata / reverte a transação que possui menos bloqueios (é menos importante, pois afetará menos linhas) e deixa a outra terminar.
Agora, vamos supor que existem dois processos A e B e 3 transações:
Esta é uma configuração muito infeliz, porque o MySQL não pode ver que há um impasse (dentro de três transações). Então, o que o MySQL faz é ... nada! Apenas espera, pois não sabe o que fazer. Ele espera até que o primeiro bloqueio adquirido exceda o tempo limite (Processo A Transação 1: Bloqueios X), e isso desbloqueia o Bloqueio X, que desbloqueia a Transação 2, etc.
A arte é descobrir o que (qual consulta) causa o primeiro bloqueio (bloqueio X). Você poderá ver facilmente (
show engine innodb status
) que a transação 3 aguarda a transação 2, mas não verá qual transação a transação 2 está aguardando (transação 1). O MySQL não imprimirá nenhum bloqueio ou consulta associada à transação 1. A única dica será que, no final da lista de transações (dashow engine innodb status
impressão), você verá a transação 1 aparentemente sem fazer nada (mas, na verdade, aguardando a transação 3 terminar).A técnica de como encontrar qual consulta SQL faz com que o bloqueio (Bloqueio X) seja concedido para uma determinada transação que está aguardando é descrita aqui
Tracking MySQL query history in long running transactions
Se você está se perguntando qual é o processo e a transação exatamente no exemplo. O processo é um processo PHP. Transação é uma transação conforme definida por innodb-trx-table . No meu caso, eu tinha dois processos PHP, em cada um deles iniciei uma transação manualmente. A parte interessante foi que, embora eu tenha iniciado uma transação em um processo, o MySQL usou internamente duas transações separadas (não faço idéia do porquê, talvez algum desenvolvedor do MySQL possa explicar).
O MySQL está gerenciando suas próprias transações internamente e decidiu (no meu caso) usar duas transações para lidar com todas as solicitações SQL provenientes do processo PHP (Processo A). A declaração de que a transação 1 está aguardando a conclusão da transação 3 é algo interno do MySQL. O MySQL "sabia" que a Transação 1 e a Transação 3 foram realmente instanciadas como parte de uma solicitação de "transação" (do Processo A). Agora toda a "transação" foi bloqueada porque a Transação 3 (uma subparte de "transação") foi bloqueada. Como "transação" não pôde concluir a Transação 1 (também uma subparte da "transação") foi marcada como não concluída também. Isso é o que eu quis dizer com "A transação 1 aguarda a conclusão da transação 3".
fonte
O grande problema dessa exceção é que geralmente não é reproduzível em um ambiente de teste e não estamos por perto para executar o status do mecanismo innodb quando ocorre no prod. Então, em um dos projetos, coloquei o código abaixo em um bloco catch para essa exceção. Isso me ajudou a entender o status do mecanismo quando a exceção aconteceu. Isso ajudou muito.
fonte
Dê uma olhada na página de manual do
pt-deadlock-logger
utilitário :Extrai informações do
engine innodb status
mencionado acima e também pode ser usado para criar umdaemon
que é executado a cada 30 segundos.fonte
Extrapolando da resposta de Rolando acima, são estas que estão bloqueando sua consulta:
Se você precisar executar sua consulta e não puder esperar que os outros sejam executados, mate-os usando o ID do thread do MySQL:
(de dentro do mysql, não do shell, obviamente)
Você precisa encontrar os IDs de thread em:
comando e descubra qual é o que está bloqueando o banco de dados.
fonte
5341773
? Não vejo o que distingue esse dos outros.Aqui está o que eu finalmente tive que fazer para descobrir o que "outra consulta" causou o problema de tempo limite de bloqueio. No código do aplicativo, rastreamos todas as chamadas pendentes do banco de dados em um thread separado dedicado a esta tarefa. Se qualquer chamada de banco de dados demorar mais de N segundos (para nós, são 30 segundos), registramos:
Com o exposto, conseguimos identificar consultas simultâneas que bloqueavam as linhas que causavam o conflito. No meu caso, eram declarações como as
INSERT ... SELECT
que, ao contrário de SELECTs simples, bloqueiam as linhas subjacentes. Em seguida, você pode reorganizar o código ou usar um isolamento de transação diferente, como a leitura não confirmada.Boa sorte!
fonte
Você pode usar:
que listará todas as conexões no MySQL e o estado atual da conexão, bem como a consulta que está sendo executada. Há também uma variante mais curta
show processlist;
que exibe a consulta truncada, bem como as estatísticas de conexão.fonte
Se você estiver usando JDBC, terá a opção
includeInnodbStatusInDeadlockExceptions = true
https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html
fonte
Ative o MySQL general.log (uso intensivo de disco) e use mysql_analyse_general_log.pl para extrair transações de execução longa, por exemplo, com:
Desative general.log depois disso.
fonte