Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

MySQL:Obtendo permanentemente Aguardando o bloqueio de metadados da tabela


A solução aceita é, infelizmente, errada . Está certo no que diz,

Isso é de fato (quase certamente; veja abaixo) o que fazer. Mas então sugere,

...e 1398 não a conexão com a fechadura. Como isso poderia ser? 1398 é a conexão aguardando para a fechadura. Isso significa que ainda não tem a fechadura e, portanto, matá-la não adianta nada. O processo que mantém o bloqueio ainda manterá o bloqueio, e o próximo thread tentando fazer algo, portanto, também stall e digite "Aguardando bloqueio de metadados" na devida ordem.

Você não tem garantia de que os processos "aguardando bloqueio de metadados" (WFML) também não serão bloqueados, mas pode ter certeza de que matar apenas processos WFML não alcançará exatamente nada .

A causa real é que outro processo está mantendo o bloqueio e, mais importante, SHOW FULL PROCESSLIST não informará diretamente qual é .

Ele VAI informar se o processo está fazendo alguma coisa, sim. Normalmente funciona. Aqui, o processo que mantém o bloqueio não está fazendo nada , e se esconde entre outros tópicos também não fazendo nada.

Neste caso, o culpado é quase certamente processo 1396 , que começou antes do processo 1398 e agora está em Sleep estado, e tem sido por 46 segundos. Desde 1396 claramente fez tudo o que precisava fazer (como provado pelo fato de que agora está dormindo, e fez isso por 46 segundos, no que diz respeito ao MySQL ), nenhum thread tendo adormecido antes que pudesse ter travado (ou 1396 também teria travado).

IMPORTANTE :se você se conectou ao MySQL como um usuário limitado, SHOW FULL PROCESSLIST não mostrar todos os processos. Portanto, o bloqueio pode ser mantido por um processo que você não vê.

Uma SHOW PROCESSLIST melhor

SELECT ID, TIME, USER, HOST, DB, COMMAND, STATE, INFO
    FROM INFORMATION_SCHEMA.PROCESSLIST WHERE DB IS NOT NULL
    AND (`INFO` NOT LIKE '%INFORMATION_SCHEMA%' OR INFO IS NULL)
    ORDER BY `DB`, `TIME` DESC

O acima pode ser ajustado para mostrar apenas os processos no estado SLEEP, e de qualquer forma ele os classificará por tempo decrescente, para que seja mais fácil encontrar o processo que está travando (geralmente é o Sleep 'ing um imediatamente antes dos "aguardando bloqueio de metadados").

O importante


Deixe qualquer processo de "aguardando bloqueio de metadados" sozinho .

Solução rápida e suja, não realmente recomendada, mas rápida


Mate todos processos no estado "Sleep", no mesmo banco de dados, que são mais antigos que os mais antigos thread no estado "aguardando bloqueio de metadados". Isto é o que Arnaud Amaury teria feito:
  • para cada banco de dados que tenha pelo menos um thread em WaitingForMetadataLock:
    • a conexão mais antiga no WFML nesse banco de dados tem Z segundos
    • TODAS as threads "Sleep" nesse banco de dados e mais antigas que Z devem ser eliminadas. Comece com os mais recentes, por precaução.
    • Se uma conexão mais antiga e inativa existe nesse banco de dados, talvez seja aquela que mantém o bloqueio, mas está fazendo alguma coisa . É claro que você pode matá-lo, mas especialmente se for um UPDATE/INSERT/DELETE, você o faz por sua conta e risco.

Noventa e nove vezes em cem, o segmento a ser morto é o mais jovem entre aqueles em estado de suspensão que são mais velhos do que o mais antigo aguardando o bloqueio de metadados:
TIME     STATUS
319      Sleep
205      Sleep
 19      Sleep                      <--- one of these two "19"
 19      Sleep                      <--- and probably this one(*)
 15      Waiting for metadata lock  <--- oldest WFML
 15      Waiting for metadata lock
 14      Waiting for metadata lock

(*) a ordem TIME na verdade tem milissegundos, ou assim me disseram, simplesmente não os mostra. Portanto, embora ambos os processos tenham um valor de Tempo de 19, o menor deve ser mais jovem.

Correção mais focada


Execute SHOW ENGINE INNODB STATUS e veja a seção "TRANSAÇÃO". Você encontrará, entre outros, algo como
TRANSACTION 1701, ACTIVE 58 sec;2 lock struct(s), heap size 376, 1 row lock(s), undo log entries 1
MySQL thread id 1396, OS thread handle 0x7fd06d675700, query id 1138 hostname 1.2.3.4 whatever;

Agora você verifica com SHOW FULL PROCESSLIST o que o ID do segmento 1396 está fazendo com sua transação #1701. As chances são de que ele esteja no status "Sleep". Então:uma transação ativa (#1701) com um bloqueio ativo, até fez algumas alterações, pois possui uma entrada de log de desfazer... mas está atualmente ociosa. Isso e nenhum outro é o segmento que você precisa matar. Perder essas mudanças.

Lembre-se que não fazer nada no MySQL não significa não fazer nada em geral. Se você obtiver alguns registros do MySQL e criar um CSV para upload por FTP, durante o upload por FTP, a conexão do MySQL ficará ociosa.

Na verdade, se o processo usando MySQL e o servidor MySQL estão na mesma máquina, essa máquina executa Linux e você tem privilégios de root, há uma maneira de descobrir qual processo tem a conexão que solicitou o bloqueio. Isso, por sua vez, permite determinar (a partir do uso da CPU ou, na pior das hipóteses, strace -ff -p pid ) se esse processo é realmente fazendo algo ou não, para ajudar a decidir se é seguro matar.

Por que isso acontece?


Vejo isso acontecendo com webapps que usam conexões MySQL "persistentes" ou "em pool", que hoje em dia geralmente economizam muito pouco tempo:a instância do webapp foi encerrada, mas a conexão não , então seu bloqueio ainda está ativo... e bloqueando todos os outros.

Outra maneira interessante que encontrei é, nas hipóteses acima, executar uma consulta retornando algumas linhas, e apenas recuperar algumas delas . Se a consulta não estiver definida como "limpeza automática" (no entanto, o DBA subjacente o fizer), ela manterá a conexão aberta e impedirá que um bloqueio completo na tabela seja realizado. Isso aconteceu comigo em um pedaço de código que verificava se uma linha existia selecionando essa linha e verificando se havia um erro (não existe) ou não (deve existir), mas sem realmente recuperar a linha .

Pergunte ao banco de dados


Outra maneira de obter o culpado se você tiver um MySQL recente, mas não muito recente já que isso será descontinuado , é (você precisa de privilégios novamente no esquema de informações)
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS 
     WHERE LOCK_TRX_ID IN 
        (SELECT BLOCKING_TRX_ID FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS);

Solução real, exigindo tempo e trabalho


O problema geralmente é causado por esta arquitetura:

Quando o webapp morre ou a instância de thread leve do webapp morre, o contêiner/pool de conexão pode não . E é o contêiner que mantém a conexão aberta, então obviamente a conexão não fecha. Muito previsivelmente, MySQL não considera a operação completa .

Se o webapp não limpar depois de si mesmo (sem ROLLBACK ou COMMIT para uma transação, não UNLOCK TABLES , etc.), então tudo o que o webapp começou a fazer ainda existe , e ainda pode estar bloqueando todos os outros.

Existem então duas soluções. O pior é diminuir o tempo limite de inatividade . Mas adivinhe o que acontece se você esperar muito tempo entre duas consultas (exatamente:"O servidor MySQL desapareceu"). Você poderia então usar mysql_ping se disponível (em breve será descontinuado. Existem soluções alternativas para DOP. Ou você pode verificar isso erro e reabra a conexão se isso acontecer (essa é a maneira do Python). Então - por uma pequena taxa de desempenho - é factível.

A solução melhor e mais inteligente é menos simples de implementar. Esforce-se para que o script seja limpo depois de si mesmo, garantindo a recuperação de todas as linhas ou a liberação de todos os recursos de consulta, capture todas as exceções e trate-as adequadamente ou, se possível, ignore completamente as conexões persistentes . Deixe que cada instância crie sua própria conexão ou use um smart motorista de piscina (no PHP PDO, use PDO::ATTR_PERSISTENT explicitamente definido como false ). Alternativamente (por exemplo, em PHP) você pode ter manipuladores de destruição e exceção para forçar a limpeza da conexão confirmando ou revertendo transações e emitindo desbloqueios de tabela explícitos.

Não conheço uma maneira de consultar os recursos do conjunto de resultados existentes para liberá-los; a única maneira seria salvar esses recursos em uma matriz privada.