AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 39235
Accepted
Mark Amery
Mark Amery
Asked: 2013-04-05 05:06:40 +0800 CST2013-04-05 05:06:40 +0800 CST 2013-04-05 05:06:40 +0800 CST

Conexão ociosa mais consulta de modificação de esquema causando banco de dados bloqueado

  • 772

Como parte de nosso processo de implantação automatizada para um aplicativo da Web em execução em uma pilha LAMP, descartamos todos os nossos gatilhos e procedimentos armazenados e os recriamos a partir do controle de origem. Acontece que havia um perigo oculto nessa abordagem que não havíamos pensado.

Alguns dias atrás, conseguimos acabar com o banco de dados para (a versão de teste de) nosso aplicativo da web travado em um estado horrivelmente travado após a seguinte sequência de eventos:

  1. Eu me conecto ao banco de dados remoto de nosso escritório (através do MySQLdb do Python, por acaso) e executo algumas consultas SELECT na tabela Foo.
  2. Deixo a conexão aberta, porque estou com preguiça.
  3. Meu chefe faz algumas alterações em seu laptop, envia para o repositório remoto no servidor da web e vai almoçar sem olhar para a saída
  4. O gancho de implantação no servidor da Web tenta atualizar os gatilhos e os procedimentos armazenados no banco de dados, mas não consegue nem DROP o primeiro gatilho porque o gatilho envolve a tabela Foo, da qual minha conexão atualmente adormecida já havia feito alguns SELECTs.
  5. Agora ninguém pode SELECT da tabela Foo, porque a conexão que está tentando DROP o gatilho já removeu um bloqueio na tabela Foo que impede qualquer outra conexão de acessar a tabela Foo de qualquer maneira - mesmo que ainda esteja esperando pelo conexão adormecida seja fechada antes que possa realmente fazer qualquer coisa.
  6. Processos de negócios cruciais que dependem da tabela Foo param, soam alarmes e nosso aplicativo da web para de atender os clientes. Meu chefe fica furioso e declara que cabeças vão rolar se a causa do problema não for encontrada e corrigida para que isso nunca mais aconteça. (Brincadeirinha, era apenas nosso servidor de teste e meu chefe é muito amigável.)

O interessante é que esse cenário não foi causado por nenhum tipo de impasse; foi causado por uma conexão adormecida segurando implicitamente algum tipo de bloqueio que impedia a DROP TRIGGERexecução da instrução, apenas por ter feito um SELECTna mesma tabela anteriormente. Nenhum dos recursos anti-deadlock do MySQL poderia matar automaticamente um processo e salvar a situação, porque no final das contas tudo poderia continuar assim que meu processo original - o ocioso que só fazia SELECTs - fosse morto. O fato de os bloqueios do MySQL se comportarem dessa maneira por padrão parece perverso para mim, mas esse não é o ponto. Estou tentando descobrir uma maneira de garantir que o cenário de desastre descrito acima não volte a ocorrer (especialmente em nosso servidor ativo). Como você sugere que eu faça isso?

Conversamos sobre o problema no escritório e vimos algumas soluções hipotéticas:

  • Altere algumas configurações em algum lugar para que os processos adormecidos expirem após 10 segundos por padrão, para que um processo adormecido nunca fique bloqueado. Melhor ainda, faça com que eles liberem todos os bloqueios após 10 segundos para que eu ainda possa ir almoçar e deixar meu shell MySQL aberto, ou minha janela Python aberta com uma conexão MySQLdb ativa, depois voltar e usá-lo, sem medo de quebrar nada .

    • Isso pode ser realmente irritante ao tentar executar consultas manualmente, especialmente aquelas que exigem agrupamento em uma transação.
  • Faça alguma mágica nas consultas que tentam substituir os gatilhos e procedimentos armazenados para que a aquisição de bloqueios necessários para os DROPs e CREATEs relevantes seja transformada em uma operação atômica - algo como, se a consulta não puder adquirir todos os bloqueios de que precisa imediatamente em sequência, então ele os libera e tenta novamente periodicamente até funcionar.

    • Isso pode fazer com que nosso processo de implantação nunca seja concluído, no entanto, se o banco de dados estiver muito ocupado para conseguir obter todos os bloqueios de uma só vez.
  • Reduza drasticamente a frequência de consultas de modificação de esquema que fazemos (parece que apenas essas podem ser bloqueadas de iniciar por uma conexão que é feita apenas SELECTs), por exemplo, fazendo com que nosso script de implantação verifique se um procedimento armazenado ou gatilho no controle de origem mudou da versão no banco de dados antes de DROPping e recriar aquela no banco de dados.

    • Isso apenas atenua o problema, na verdade não o elimina.

Não temos certeza se alguma das duas primeiras soluções que consideramos são possíveis no MySQL, ou se estamos perdendo uma solução melhor (somos desenvolvedores, não DBAs, e isso está fora de nossa zona de conforto). O que você recomendaria?

mysql locking
  • 2 2 respostas
  • 1543 Views

2 respostas

  • Voted
  1. Michael - sqlbot
    2013-04-06T16:26:40+08:002013-04-06T16:26:40+08:00

    O fato de os bloqueios do MySQL se comportarem dessa maneira por padrão parece perverso para mim, mas esse não é o ponto.

    Na verdade, esse é totalmente o ponto, porque bloqueios de SELECTinstruções é algo que o MySQL normalmente não faz ... então, por algum mecanismo ainda desconhecido, você pediu para fazer isso.

    A explicação mais provável é que você (ou o que quer que esteja usando como cliente, possivelmente não intencionalmente de sua perspectiva) iniciou uma transação que não foi confirmada ou revertida e fez as seleções no contexto dessa transação, ou você desativou o autocommit .

    Se tudo o que você fez foram SELECTdeclarações, esta é a única explicação que posso apresentar, porque fora de uma transação, isso não poderia acontecer e não há outro motivo dentro de uma transação para o InnoDB ter bloqueado os metadados da tabela devido a simples SELECT. Na verdade, até agora, só consegui duplicar isso usando o SERIALIZABLEnível de isolamento.

    A maior parte do restante desta discussão assume que você está usando InnoDB. Se não for esse o caso, estou realmente perdido, porque não há nada sobre um SELECTque possa bloquear uma tabela em um mecanismo de armazenamento não transacional.

    Entender o que realmente causou esses bloqueios deve aproximá-lo de evitar o problema no futuro.

    Altere algumas configurações em algum lugar para que os processos adormecidos expirem após 10 segundos por padrão, para que um processo adormecido nunca fique bloqueado.

    Você tecnicamente pode fazer isso, mas não faça. Se você fizer isso, perderá qualquer valor real do pool de conexões e absolutamente nunca, jamais, poderá permitir a reconexão automática com segurança, porque descobrirá que as transações que você pensava que estavam abertas desapareceram, os bloqueios que você pensou que mantinha agora estão ausentes, a sessão as variáveis ​​que você pensou em definir agora são NULL. Não, não tente isso.

    Melhor ainda, faça com que eles liberem todos os bloqueios após 10 segundos

    Felizmente, este é impossível, o que é bom, porque então você não teria nada para dizer se ainda mantém as fechaduras que pensava ter intencionalmente.

    Mas nenhuma dessas coisas deve ser uma necessidade se você puder identificar o que seu cliente está fazendo que está causando bloqueios em SELECT.

    Seu servidor provavelmente tem a tabela information_schema.innodb_trx , e uma consulta dessa tabela seria um bom teste - embora não à prova de falhas - antes de alternar suas alterações de esquema. Se houver alguma transação, provavelmente você deve esperar até que não haja.

    Você quase certamente deve bloquear suas tabelas com WRITEbloqueios antes de começar a soltar os gatilhos, pois sempre há a possibilidade de que uma consulta possa ser inserida/atualizada/excluída durante o curto período de tempo em que o gatilho desaparece até que você o coloque de volta.

    Se você bloquear todas as tabelas com uma instrução, ele obterá bloqueios individualmente conforme eles estiverem disponíveis e bloqueará até que todos os bloqueios possam ser obtidos... mesa e, em seguida, prosseguir para a próxima mesa, você deve ser bom, assumindo que suas transações em outras sessões fazem o que as transações devem fazer - entre, trabalhe, saia, não fique por perto - mas em qualquer caso, tudo (lock, drop trigger, create trigger, unlock table) deve ser feito em uma sessão -- na mesma conexão -- onde você obteve o(s) lock(s).

    Menos confuso pode ser bloquear uma tabela, fazer alterações e desbloqueá-la novamente.

    Há uma frase na documentação sobre bloqueio de tabela e interação de transação que é ambígua:

    LOCK TABLES não é seguro para transações e implicitamente confirma qualquer transação ativa antes de tentar bloquear as tabelas.

    O "qualquer" nessa frase é enganoso. Não significa qualquer transação no servidor, refere-se apenas a qualquer transação que você tenha ativa na sessão em que emitir o LOCK TABLESextrato, que não deveria haver.

    fazer com que nosso script de implantação verifique se um procedimento armazenado ou gatilho no controle de origem mudou da versão no banco de dados

    De qualquer forma, essa é realmente uma boa ideia, porque quanto mais você mexe em um sistema ativo, mais provável é que as coisas possam dar errado... inconsistências que podem surgir durante essas minúsculas janelas de tempo, já que uma combinação de DROPe CREATEnão pode ser feita em conjunto, atomicamente.

    • 3
  2. Best Answer
    Ewen Cheslack-Postava
    2013-08-30T16:51:33+08:002013-08-30T16:51:33+08:00

    Isso provavelmente ocorre porque a confirmação automática está desativada por padrão, conforme especificado pelo PEP 249. Isso parece fazer com que qualquer SELECT bloqueie a tabela de metadados. Provavelmente, você pode ativar a confirmação automática (desde que seja seguro com base no código do aplicativo), o que fechará a transação implícita associada ao SELECT imediatamente. Como alternativa, use transações explícitas.

    • 2

relate perguntas

  • Existem ferramentas de benchmarking do MySQL? [fechado]

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

  • Quando é o momento certo para usar o MariaDB em vez do MySQL e por quê?

  • Como um grupo pode rastrear alterações no esquema do banco de dados?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Conceder acesso a todas as tabelas para um usuário

    • 5 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve