ALTER TABLE sem travar a mesa?

107

Ao fazer uma instrução ALTER TABLE no MySQL, toda a tabela é bloqueada para leitura (permitindo leituras simultâneas, mas proibindo gravações simultâneas) durante a instrução. Se for uma mesa grande, as instruções INSERT ou UPDATE podem ser bloqueadas por muuuuito tempo. Existe uma maneira de fazer uma "alteração quente", como adicionar uma coluna de forma que a tabela ainda seja atualizável durante o processo?

Em geral, estou interessado em uma solução para MySQL, mas estaria interessado em outro RDBMS se o MySQL não pudesse fazer isso.

Para esclarecer, meu objetivo é simplesmente evitar o tempo de inatividade quando um novo recurso que requer uma coluna de tabela extra é colocado em produção. Qualquer esquema de banco de dados vai mudar ao longo do tempo, isso é apenas um fato da vida. Não vejo por que devemos aceitar que essas mudanças resultem inevitavelmente em tempo de inatividade; isso é apenas fraco.

Daniel
fonte
2
Já se perguntou quantas vezes você alterará a mesa?
Allain Lalonde
1
IMHO, as mudanças no esquema do banco de dados estão associadas a novas versões inteiras - elas não são lançadas esporadicamente como outras mudanças. É inevitavelmente um grande negócio.
dkretz
9
@AllainLalonde - mais de 0 vezes torna esta pergunta legítima, especialmente se o tempo de inatividade em seu sistema custar vidas ou muito dinheiro. E, de qualquer forma, novos requisitos de software aparecem às vezes.
Nathan Long

Respostas:

60

A única outra opção é fazer manualmente o que muitos sistemas RDBMS fazem de qualquer maneira ...
- Criar uma nova tabela

Você pode então copiar o conteúdo da tabela antiga sobre um pedaço de cada vez. Embora sempre seja cauteloso com qualquer INSERT / UPDATE / DELETE na tabela de origem. (Pode ser gerenciado por um gatilho. Embora isso cause uma lentidão, não é um bloqueio ...)

Quando terminar, altere o nome da tabela de origem e, em seguida, altere o nome da nova tabela. De preferência em uma transação.

Quando terminar, recompile todos os procedimentos armazenados, etc. que usam essa tabela. Os planos de execução provavelmente não serão mais válidos.

EDITAR:

Alguns comentários foram feitos sobre esta limitação ser um pouco pobre. Então eu pensei em colocar uma nova perspectiva sobre isso para mostrar por que é assim ...

  • Adicionar um novo campo é como alterar um campo em cada linha.
  • Os bloqueios de campo seriam muito mais difíceis do que os bloqueios de linha, quanto mais os bloqueios de mesa.

  • Na verdade, você está mudando a estrutura física do disco, cada registro se move.
  • Isso realmente é como uma atualização na tabela inteira, mas com mais impacto ...
MatBailie
fonte
2
E tenha um plano de teste completo antes de trocar. Se falhar, recomece.
dkretz
2
Gerenciar a sincronização por meio de gatilhos foi uma boa ideia. Estou usando o MySQL há tanto tempo que sempre esqueço que agora eles têm gatilhos. Usei essa técnica e agora tenho um script de alteração ativa funcional. Com uma barra de progresso. E funciona com MyISAM. A vida é boa.
Daniel
2
+1 Isso é literalmente o que o SQL Enterprise Manager faz nos bastidores quando você faz certos tipos de mudanças de tabela na IU. No SQL 2008, eles adicionaram um aviso para que o usuário SAIBA que está realizando esta ação drástica.
BradC
2
Você não mencionou nada sobre as chaves estrangeiras que fazem referência às tabelas que estão sendo alteradas. Isso não seria um problema?
Rafay
2
@MohammadRafayAleem - E campos AUTOINCREMENT, visualizações e gatilhos, etc, etc. Mas mesmo assim, a abordagem ainda é viável.
MatBailie
42

Percona faz uma ferramenta chamada pt-online-schema-change que permite que isso seja feito.

Essencialmente, faz uma cópia da tabela e modifica a nova tabela. Para manter a nova tabela sincronizada com a original, ela usa gatilhos para atualizar. Isso permite que a tabela original seja acessada enquanto a nova tabela é preparada em segundo plano.

Isso é semelhante ao método sugerido pelo Dems acima, mas de maneira automatizada.

Algumas de suas ferramentas têm uma curva de aprendizado, ou seja, conectar-se ao banco de dados, mas uma vez que você tenha feito isso, elas são ótimas ferramentas.

Ex:

pt-online-schema-change --alter "ADD COLUMN c1 INT" D=db,t=numbers_are_friends
SeanDowney
fonte
Parece que o link está quebrado. Eu descobri que este link está funcionando.
Noam Ben Ari
25

Esta questão de 2009. Agora o MySQL oferece uma solução:

DDL online (linguagem de definição de dados)

Um recurso que melhora o desempenho, a simultaneidade e a disponibilidade das tabelas InnoDB durante as operações DDL (principalmente ALTER TABLE). Consulte a Seção 14.11, “InnoDB e DDL online” para obter os detalhes.

Os detalhes variam de acordo com o tipo de operação. Em alguns casos, a tabela pode ser modificada simultaneamente enquanto ALTER TABLE está em andamento. A operação pode ser executada sem fazer uma cópia da tabela ou usando um tipo especialmente otimizado de cópia da tabela. O uso do espaço é controlado pela opção de configuração innodb_online_alter_log_max_size.

Ele permite que você ajuste o equilíbrio entre desempenho e simultaneidade durante a operação DDL, escolhendo se deseja bloquear o acesso à tabela inteiramente (LOCK = cláusula EXCLUSIVE), permitir consultas, mas não DML (LOCK = cláusula SHARED) ou permitir consulta completa e DML acesso à tabela (cláusula LOCK = NONE). Quando você omite a cláusula LOCK ou especifica LOCK = DEFAULT, o MySQL permite o máximo de simultaneidade possível, dependendo do tipo de operação.

Executar mudanças no local onde possível, em vez de criar uma nova cópia da tabela, evita aumentos temporários no uso de espaço em disco e sobrecarga de E / S associada à cópia da tabela e reconstrução de índices secundários.

consulte o Manual de Referência do MySQL 5.6 -> InnoDB e DDL online para obter mais informações.

Parece que o DDL online também está disponível no MariaDB

Alternativamente, você pode usar ALTER ONLINE TABLE para garantir que seu ALTER TABLE não bloqueie operações simultâneas (sem bloqueios). É equivalente a LOCK = NONE.

MariaDB KB sobre ALTER TABLE

Ivanov
fonte
3
É uma pena que não haja outra maneira a não ser votos para colocar isso no topo, visto que na maioria das vezes nega todas as outras respostas puramente porque elas não fazem mais referência à versão atual do MySQL.
Burhan Ali,
14

Eu recomendo Postgres se essa for uma opção. Com o postgres, essencialmente não há tempo de inatividade com os seguintes procedimentos:

Outro ótimo recurso é que a maioria das instruções DDL são transacionais, então você pode fazer uma migração inteira dentro de uma transação SQL e, se algo der errado, tudo será revertido.

Eu escrevi isso um pouco atrás, talvez possa esclarecer melhor os outros méritos.

mikelikespie
fonte
6
O Postgres ainda cria um bloqueio exclusivo no alter, evitando que outros leiam aquela tabela.
clofresh
5
Não concordo com a parte "essencialmente sem tempo de inatividade". Como clofresh disse, ALTER TABLE captura um bloqueio exclusivo na tabela, bloqueando todas as leituras e gravações simultâneas. Na minha experiência, para tabelas ativas na maioria das vezes você nem mesmo obterá o bloqueio (ALTER TABLE morrerá de fome). E com as transações, você pode facilmente acabar em impasses se não for extremamente cuidadoso. Por causa disso, agora sempre defino tempos de inatividade ao alterar tabelas existentes no Postgres.
Pankrat
1
uma explicação mais detalhada: dba.stackexchange.com/questions/27153/… menciona as implicações do bloqueio exclusivo e algumas maneiras de contornar isso
John Douthat
4
Sim, alterar uma tabela no postgres obtém um bloqueio exclusivo, mas como a operação em si é concluída em milissegundos, isso é praticamente irrelevante na maioria dos casos. Eu pessoalmente adicionei colunas a tabelas de cem milhões de linhas no meio do dia útil com zero tempo de inatividade resultante.
Noah Yetter
2
@cobbzilla Sim, DROP COLUMN é tão rápido quanto. Sob o capô, o que basicamente faz é marcar a coluna como oculta. Os valores que existiam naquela coluna antes de ser eliminada ainda estão nos arquivos de dados (e visíveis para outras transações) e permanecerão assim, a menos e até que você execute um VACUUM FULL.
Noah Yetter de
7

Como você perguntou sobre outros bancos de dados, aqui estão algumas informações sobre o Oracle.

Adicionar uma coluna NULL a uma tabela Oracle é uma operação muito rápida, pois apenas atualiza o dicionário de dados. Isso mantém um bloqueio exclusivo na mesa por um período muito curto de tempo. No entanto, isso invalidará quaisquer procedimentos armazenados, visualizações, gatilhos, etc. Depedant. Eles serão recompilados automaticamente.

A partir daí, se necessário, você pode criar um índice usando a cláusula ONLINE. Novamente, apenas bloqueios de dicionário de dados muito curtos. Ele lerá toda a tabela procurando coisas para indexar, mas não bloqueará ninguém enquanto faz isso.

Se precisar adicionar uma chave estrangeira, você pode fazer isso e fazer com que o Oracle confie em você de que os dados estão corretos. Caso contrário, ele precisa ler toda a tabela e validar todos os valores que podem ser lentos (crie seu índice primeiro).

Se você precisar colocar um valor padrão ou calculado em cada linha da nova coluna, você precisará executar uma atualização massiva ou talvez um pequeno programa utilitário que preencha os novos dados. Isso pode ser lento, especialmente se as linhas ficarem muito maiores e não couberem mais em seus blocos. O bloqueio pode ser gerenciado durante este processo. Como o antigo versino de seu aplicativo, que ainda está em execução, não conhece esta coluna, você pode precisar de um gatilho sorrateiro ou para especificar um padrão.

A partir daí, você pode fazer um switcharoo em seus servidores de aplicativos para a nova versão do código e ele continuará em execução. Solte seu gatilho sorrateiro.

Alternativamente, você pode usar DBMS_REDEFINITION, que é uma caixa preta projetada para fazer esse tipo de coisa.

Tudo isso é tão trabalhoso para testar, etc, que apenas temos uma interrupção no início da manhã de domingo sempre que lançamos uma versão principal.

WW.
fonte
3

Se você não pode permitir o tempo de inatividade do seu banco de dados ao fazer atualizações de aplicativos, deve considerar a manutenção de um cluster de dois nós para alta disponibilidade. Com uma configuração de replicação simples, você pode fazer mudanças estruturais quase totalmente online como a que você sugere:

  • espere que todas as mudanças sejam replicadas em um escravo passivo
  • mude o escravo passivo para ser o mestre ativo
  • fazer as mudanças estruturais no velho mestre
  • replicar as mudanças do novo mestre para o antigo mestre
  • faça a troca mestre novamente e a nova implantação do aplicativo simultaneamente

Nem sempre é fácil, mas funciona, geralmente com 0 tempo de inatividade! O segundo nó não precisa ser apenas um nó passivo, ele pode ser usado para testar, fazer estatísticas ou como um nó de fallback. Se você não tiver infraestrutura, a replicação pode ser configurada em uma única máquina (com duas instâncias do MySQL).

jynus
fonte
1
O antigo mestre está fora do cluster ou dentro do cluster?
John Chornelius
2

Não. Se você estiver usando tabelas MyISAM, para meu melhor entendimento, eles só fazem bloqueios de tabela - não há bloqueios de registro, eles apenas tentam manter tudo hiper-rápido por meio da simplicidade. (Outras tabelas do MySQL operam de maneira diferente.) Em qualquer caso, você pode copiar a tabela para outra tabela, alterá-la e, em seguida, trocá-las, atualizando as diferenças.

Essa é uma alteração tão grande que duvido que algum DBMS a suporte. É considerado uma vantagem poder fazer isso com os dados da tabela em primeiro lugar.

dkretz
fonte
InnoDB usa travas de linha - dev.mysql.com/doc/refman/5.0/en/internal-locking.html
Eran Galperin
Sim, o MySQL é a aberração. É por isso que fui específico sobre as tabelas "padrão".
dkretz
Você escreveu - tabelas padrão do MySQL apenas bloqueiam a tabela - o que é incorreto.
Eran Galperin
Como você interpreta isso sobre as tabelas MyISAM (ou seja, o padrão MySQL) da página que você citou? "O MySQL usa bloqueio em nível de tabela para tabelas MyISAM e MEMORY, bloqueio em nível de página para tabelas BDB e bloqueio em nível de linha para tabelas InnoDB."
dkretz
alguns mecanismos de armazenamento usam bloqueio de nível de linha e alguns usam bloqueio de nível de tabela. Não há mecanismo de armazenamento padrão (talvez você quisesse dizer o padrão no phpMyAdmin ...)
Eran Galperin
2

Solução temporária...

Outra solução poderia ser adicionar outra tabela com a chave primária da tabela original, junto com sua nova coluna.

Preencha sua chave primária na nova tabela e preencha valores para a nova coluna em sua nova tabela e modifique sua consulta para unir esta tabela para operações selecionadas e você também precisa inserir, atualizar separadamente para este valor de coluna.

Quando você consegue obter um tempo de inatividade, pode alterar a tabela original, modificar suas consultas DML e descartar sua nova tabela criada anteriormente

Caso contrário, você pode ir para o método de clustering, replicação, ferramenta pt-online-schema de percona

Balasundaram
fonte
1

Usando o plugin Innodb, as instruções ALTER TABLE que apenas adicionam ou descartam índices secundários podem ser feitas "rapidamente", ou seja, sem reconstruir a tabela.

De um modo geral, entretanto, no MySQL, qualquer ALTER TABLE envolve a reconstrução de toda a tabela, o que pode levar muito tempo (ou seja, se a tabela contiver uma quantidade útil de dados).

Você realmente precisa projetar seu aplicativo de forma que as instruções ALTER TABLE não precisem ser feitas regularmente; você certamente não deseja que nenhum ALTER TABLE seja executado durante a execução normal do aplicativo, a menos que esteja preparado para esperar ou alterando tabelas minúsculas.

MarkR
fonte
1

Eu recomendaria uma de duas abordagens:

  1. Projete suas tabelas de banco de dados com as possíveis mudanças em mente. Por exemplo, trabalhei com sistemas de gerenciamento de conteúdo, que alteram campos de dados no conteúdo regularmente. Em vez de construir a estrutura física do banco de dados para corresponder aos requisitos iniciais do campo CMS, é muito melhor construir uma estrutura flexível. Nesse caso, usando um campo de texto blob (varchar (max) por exemplo) para manter dados XML flexíveis. Isso torna as mudanças estruturais muito menos frequentes. Mudanças estruturais podem ser caras, portanto, há um benefício no custo aqui também.

  2. Tem tempo de manutenção do sistema. O sistema fica offline durante as mudanças (mensalmente, etc), e as mudanças são programadas durante o horário do dia com menos tráfego (3 a 5 da manhã, por exemplo). As mudanças são preparadas antes do lançamento da produção, então você terá uma boa estimativa de janela fixa de tempo de inatividade.

2a. Ter servidores redundantes, para que quando o sistema ficar inativo, todo o site não fique inativo. Isso permitiria a você "lançar" suas atualizações de forma escalonada, sem tirar todo o site do ar.

As opções 2 e 2a podem não ser viáveis; eles tendem a ser apenas para sites / operações maiores. No entanto, são opções válidas e usei pessoalmente todas as opções apresentadas aqui.

Pearcewg
fonte
1

Se alguém ainda estiver lendo isso ou vier aqui, este é o grande benefício de usar um sistema de banco de dados NoSQL como o mongodb. Tive o mesmo problema ao lidar com a alteração da tabela para adicionar colunas para recursos adicionais ou índices em uma grande tabela com milhões de linhas e altas gravações. Isso acabaria travando por um longo tempo, então fazer isso no banco de dados LIVE frustraria nossos usuários. Em mesas pequenas, você pode se safar.

Odeio o fato de termos que "projetar nossas tabelas para evitar alterá-las". Só não acho que isso funcione no mundo dos sites de hoje. Você não pode prever como as pessoas usarão seu software, por isso você muda rapidamente as coisas com base no feedback do usuário. Com mongodb, você pode adicionar "colunas" à vontade, sem tempo de inatividade. Você nem mesmo os adiciona, apenas insere dados com novas colunas e isso faz isso automaticamente.

Vale a pena conferir: www.mongodb.com

Brian Gruber
fonte
2
O MySQL ainda é usado em muitos sistemas, então a questão realmente é como conseguir uma mudança de esquema no SQL RDBMS, embora eu também seja um grande defensor do NoSQL.
Alexy
1

Em geral, a resposta será "Não". Você está mudando a estrutura da tabela, o que potencialmente exigirá muitas atualizações "e eu definitivamente concordo com isso. Se você espera fazer isso com frequência, então vou oferecer uma alternativa para colunas" fictícias "- use VIEWs em vez de tabelas para SELECTinserir dados. IIRC, alterar a definição de uma visão é relativamente leve e a indireção por meio de uma visão é feita quando o plano de consulta é compilado. A despesa é que você teria que adicionar a coluna a uma nova tabela e fazer o visualizar JOINna coluna.

Claro que isso só funciona se você puder usar chaves estrangeiras para executar o cascateamento de exclusões e outros enfeites. O outro bônus é que você pode criar uma nova tabela contendo uma combinação de dados e apontar a visão para ela sem perturbar o uso do cliente.

Apenas um pensamento.

D.Shawley
fonte
1

A diferença entre o Postgres e o MySQL a esse respeito é que no Postgres ele não recria uma tabela, mas modifica o dicionário de dados que é semelhante ao Oracle. Portanto, a operação é rápida, embora ainda exija a alocação de um bloqueio de tabela DDL exclusivo por um período muito curto, conforme afirmado acima por outros.

No MySQL, a operação copiará os dados para uma nova tabela enquanto bloqueia as transações, o que era o principal problema para os DBAs do MySQL antes da v. 5.6.

A boa notícia é que, desde o lançamento do MySQL 5.6, a restrição foi praticamente suspensa e agora você pode desfrutar do verdadeiro poder do banco de dados MYSQL.

Dmitriy Royzenberg
fonte
3
Parece que você estava tentando fazer um link para uma referência sobre uma alteração no MySql 5.6, mas não funcionou. Por favor, tente novamente.
dg99
1

Você definitivamente deveria tentar pt-online-schema-change. Tenho usado essa ferramenta para fazer migrações no AWS RDS com vários escravos e tem funcionado muito bem para mim. Escrevi um post elaborado no blog sobre como fazer isso que pode ser útil para você.

Blog: http://mrafayaleem.com/2016/02/08/live-mysql-schema-changes-with-percona/

Rafay
fonte
0

As colunas fictícias são uma boa ideia se você puder prever seu tipo (e torná-las anuláveis). Verifique como seu mecanismo de armazenamento lida com nulos.

O MyISAM irá bloquear tudo se você sequer mencionar o nome de uma mesa de passagem, no telefone, no aeroporto. Simplesmente faz isso ...

Dito isso, os bloqueios não são realmente um grande negócio; contanto que você não esteja tentando adicionar um valor padrão para a nova coluna a cada linha, mas deixe-o como nulo, e seu mecanismo de armazenamento seja inteligente o suficiente para não ir gravá-lo, você deve estar ok com um bloqueio que é apenas mantida por tempo suficiente para atualizar os metadados. Se você tentar escrever um novo valor, bem, você está frito.

SquareCog
fonte
1
Tentei adicionar uma coluna NULL a uma tabela InnoDB e tive que reconstruir a tabela inteira; não uma simples operação de "atualização de metadados".
Daniel
Acho que a ideia era incluir colunas extras anuláveis ​​no banco de dados quando ele fosse projetado, de modo que, se um novo recurso for necessário, alguém possa "adicionar" uma nova coluna simplesmente começando a usá-la. Não terá um nome legal, mas se o tipo de dados foi escolhido / previsto corretamente, deve funcionar.
supercat de
0

TokuDB pode adicionar / eliminar colunas e adicionar índices "quentes", a tabela está totalmente disponível durante todo o processo. Ele está disponível em www.tokutek.com

tmcallaghan
fonte
-6

Na verdade não.

Afinal, você ESTÁ alterando a estrutura subjacente da tabela, e essa é uma informação muito importante para o sistema subjacente. Você também (provavelmente) está movendo muitos dos dados no disco.

Se você planeja fazer muito isso, é melhor simplesmente preencher a tabela com colunas "fictícias" que estão disponíveis para uso futuro.

Will Hartung
fonte
3
Preencher uma tabela com colunas fictícias parece ser uma péssima ideia.
Quase