socialgekon.com
  • Principal
  • Design De Marca
  • Outros Do Mundo
  • Gerenciamento De Projetos
  • Vizinhos
Ciência De Dados E Bancos De Dados

Migrações de banco de dados: transformando lagartas em borboletas

Os usuários não se importam com o que está dentro do software que usam; apenas que funciona perfeitamente, com segurança e sem obstruções. Os desenvolvedores se esforçam para que isso aconteça, e um dos problemas que tentam resolver é como garantir que o armazenamento de dados esteja em um estado apropriado para a versão atual do produto. O software evolui e seu modelo de dados também pode mudar ao longo do tempo, por exemplo, para corrigir erros de design . Para complicar ainda mais o problema, você pode ter vários ambientes de teste ou clientes que migram para versões mais novas do produto em ritmos diferentes. Você não pode apenas documentar a estrutura da loja e quais manipulações são necessárias para usar a nova versão brilhante de uma única perspectiva.

Migrações de banco de dados: transformando lagartas em borboletas

Uma vez entrei em um projeto com alguns bancos de dados com estruturas que eram atualizadas sob demanda, diretamente pelos desenvolvedores. Isso significava que não havia uma maneira óbvia de descobrir quais mudanças precisavam ser aplicadas para migrar a estrutura para a versão mais recente e não havia nenhum conceito de controle de versão! Isso foi durante a era pré-DevOps e seria considerado uma bagunça total hoje em dia. Decidimos desenvolver uma ferramenta que seria usada para aplicar todas as alterações ao banco de dados fornecido. Ele tinha migrações e documentaria as mudanças de esquema. Isso nos deixou confiantes de que não haveria mudanças acidentais e o estado do esquema seria previsível.



Neste artigo, daremos uma olhada em como aplicar migrações de esquema de banco de dados relacional e como superar problemas concomitantes.

Em primeiro lugar, o que são migrações de banco de dados? No contexto deste artigo, um migração é um conjunto de mudanças que devem ser aplicadas a um banco de dados. Criar ou eliminar uma tabela, coluna ou índice são exemplos comuns de migrações. A forma de seu esquema pode mudar drasticamente com o tempo, especialmente se o desenvolvimento foi iniciado quando os requisitos ainda eram vagos. Portanto, ao longo de vários marcos no caminho para um lançamento, seu modelo de dados terá evoluído e pode ter se tornado completamente diferente do que era no início. As migrações são apenas etapas para o estado de destino.

Para começar, vamos explorar o que temos em nossa caixa de ferramentas para evitar reinventar o que já foi feito bem.

Ferramentas

Em todas as linguagens amplamente utilizadas, existem bibliotecas que ajudam a facilitar as migrações de banco de dados. Por exemplo, no caso do Java, as opções populares são Liquibase e Flyway . Usaremos o Liquibase mais em exemplos, mas os conceitos se aplicam a outras soluções e não estão vinculados ao Liquibase.

Por que se preocupar em usar uma biblioteca de migração de esquema separada se alguns ORMs já fornecem uma opção para atualizar automaticamente um esquema e torná-lo compatível com a estrutura das classes mapeadas? Na prática, essas migrações automáticas fazem apenas alterações de esquema simples, por exemplo, criando tabelas e colunas, e não podem fazer coisas potencialmente destrutivas, como descartar ou renomear objetos de banco de dados. Portanto, as soluções não automáticas (mas ainda automatizadas) geralmente são uma escolha melhor porque você é forçado a descrever a lógica de migração sozinho e sabe exatamente o que vai acontecer com o seu banco de dados.

Também é uma ideia muito ruim misturar modificações de esquema manuais e automatizadas porque você pode produzir esquemas únicos e imprevisíveis se as alterações manuais forem aplicadas na ordem errada ou não forem aplicadas, mesmo que sejam necessárias. Depois de escolher a ferramenta, use-a para aplicar todas as migrações de esquema.

Migrações de banco de dados típicas

As migrações típicas incluem a criação de sequências, tabelas, colunas, chaves primárias e estrangeiras, índices e outros objetos de banco de dados. Para os tipos mais comuns de mudanças, Liquibase fornece elementos declarativos distintos para descrever o que deve ser feito. Seria muito enfadonho ler sobre todas as mudanças triviais suportadas pelo Liquibase ou outras ferramentas semelhantes. Para ter uma ideia da aparência dos conjuntos de alterações, considere o seguinte exemplo em que criamos uma tabela (as declarações de namespace XML são omitidas por questões de brevidade):

createTable

Como você pode ver, o changelog é um conjunto de changesets, e changesets consistem em mudanças. Mudanças simples como UPDATE product SET code = 'new_' || code podem ser combinados para implementar migrações mais complexas; por exemplo, suponha que você precise atualizar o código do produto para todos os produtos. Isso pode ser facilmente alcançado com a seguinte alteração:

createTable

O desempenho será prejudicado se você tiver zilhões de produtos. Para acelerar a migração, podemos reescrevê-lo nas seguintes etapas:

  1. Crie uma nova tabela para produtos com PRODUCT_TMP, como vimos anteriormente. Nesta fase, é melhor criar o mínimo de restrições possível. Vamos nomear a nova tabela PRODUCT_TMP.
  2. Preencher INSERT INTO ... SELECT ... com SQL na forma de sql usando addNotNullConstraint mudança.
  3. Crie todas as restrições (addUniqueConstraint, addForeignKeyConstraint, createIndex) e índices (PRODUCT) de que você precisa.
  4. Renomeie o PRODUCT_BAK tabela para algo como renameTable. A Liquibase pode fazer isso com PRODUCT_TMP.
  5. Renomear PRODUCT para renameTable (novamente, usando PRODUCT_BAK).
  6. Opcionalmente, remova dropTable com ... .

Claro, é melhor evitar essas migrações, mas é bom saber como implementá-las no caso de você se deparar com um daqueles raros casos em que precisa.

Se você considerar XML, JSON ou YAML muito bizarro para a tarefa de descrever mudanças, então apenas usar SQL simples e utilizar todos os recursos específicos do fornecedor do banco de dados. Além disso, você pode implementar qualquer lógica customizada em Java puro.

A maneira como a Liquibase o isenta de escrever SQL específico para banco de dados real pode levar ao excesso de confiança, mas você não deve se esquecer das peculiaridades do banco de dados de destino; por exemplo, quando você cria uma chave estrangeira, um índice pode ou não ser criado, dependendo do sistema de gerenciamento de banco de dados específico sendo usado. Como resultado, você pode se encontrar em uma situação embaraçosa. Liquibase permite que você especifique que um changeset deve ser executado apenas para um tipo específico de banco de dados, por exemplo, PostgreSQL, Oracle ou MySQL. Isso torna isso possível usando os mesmos conjuntos de alterações independentes do fornecedor para bancos de dados diferentes e para outros conjuntos de alterações, usando sintaxe e recursos específicos do fornecedor. O conjunto de alterações a seguir será executado apenas se estiver usando um banco de dados Oracle:

IDX__

Além de Oracle, Liquibase suporta alguns outros bancos de dados sai da caixa.

Nomeando objetos de banco de dados

Cada objeto de banco de dados que você cria precisa ser nomeado. Você não é obrigado a fornecer explicitamente um nome para alguns tipos de objetos, por exemplo, para restrições e índices. Mas isso não significa que esses objetos não terão nomes; seus nomes serão gerados pelo banco de dados de qualquer maneira. O problema surge quando você precisa fazer referência a esse objeto para descartá-lo ou alterá-lo. Portanto, é melhor dar-lhes nomes explícitos. Mas existem regras sobre quais nomes dar? A resposta é curta: seja consistente; por exemplo, se você decidiu nomear índices como este: CODE, então um índice para o mencionado IDX_PRODUCT_CODE coluna deve ser nomeada DATABASECHANGELOG.

As convenções de nomenclatura são incrivelmente controversas, então não vamos presumir dar instruções abrangentes aqui. Seja consistente, respeite as convenções de sua equipe ou projeto, ou apenas invente-as se não houver nenhuma.

Organizando conjuntos de alterações

A primeira coisa a decidir é onde armazenar os changesets. Existem basicamente duas abordagens:

  1. Mantenha conjuntos de alterações com o código do aplicativo. É conveniente fazer isso porque você pode confirmar e revisar conjuntos de alterações e código do aplicativo juntos.
  2. Mantenha os conjuntos de alterações e o código do aplicativo separados , por exemplo, em repositórios VCS separados. Essa abordagem é adequada quando o modelo de dados é compartilhado entre vários aplicativos e é mais conveniente armazenar todos os conjuntos de alterações em um repositório dedicado e não espalhá-los por vários repositórios onde reside o código do aplicativo.

Onde quer que você armazene os conjuntos de alterações, geralmente é razoável dividi-los nas seguintes categorias:

  1. Migrações independentes que não afetam o sistema em execução. Geralmente, é seguro criar novas tabelas, sequências, etc., se o aplicativo atualmente implantado ainda não tiver conhecimento delas.
  2. Modificações de esquema que alteram a estrutura da loja , por exemplo, adicionando ou eliminando colunas e índices. Essas alterações não devem ser aplicadas enquanto uma versão mais antiga do aplicativo ainda estiver em uso, pois isso pode causar bloqueios ou comportamento estranho devido a alterações no esquema.
  3. Migrações rápidas que inserem ou atualizam pequenas quantidades de dados. Se vários aplicativos estiverem sendo implantados, os conjuntos de alterações desta categoria podem ser executados simultaneamente sem degradar o desempenho do banco de dados.
  4. Migrações potencialmente lentas que inserem ou atualizam muitos dados. É melhor aplicar essas alterações quando nenhuma outra migração semelhante estiver sendo executada.

representação gráfica das quatro categorias

Esses conjuntos de migrações devem ser executados consecutivamente antes de implantar uma versão mais recente de um aplicativo. Essa abordagem se torna ainda mais prática se um sistema é composto de vários aplicativos separados e alguns deles usam o mesmo banco de dados. Caso contrário, vale a pena separar apenas os conjuntos de alterações que podem ser aplicados sem afetar os aplicativos em execução, e os conjuntos de alterações restantes podem ser aplicados juntos.

Para aplicativos mais simples, o conjunto completo de migrações necessárias pode ser aplicado na inicialização do aplicativo. Nesse caso, todos os changesets se enquadram em uma única categoria e são executados sempre que o aplicativo é inicializado.

Qualquer que seja o estágio escolhido para aplicar as migrações, vale a pena mencionar que usar o mesmo banco de dados para vários aplicativos pode causar bloqueios quando as migrações estão sendo aplicadas. Liquibase (como muitas outras soluções semelhantes) utiliza duas tabelas especiais para registrar seus metadados: DATABASECHANGELOGLOCK e runOnChange='true'. O primeiro é usado para armazenar informações sobre os conjuntos de alterações aplicados e o último para evitar migrações simultâneas dentro do mesmo esquema de banco de dados. Portanto, se vários aplicativos devem usar o mesmo esquema de banco de dados por algum motivo, é melhor usar nomes não padrão para tabelas de metadados para evitar bloqueios.

Agora que a estrutura de alto nível está clara, você precisa decidir como organizar os conjuntos de alterações em cada categoria.

amostra de organização de conjunto de mudanças

Depende muito dos requisitos específicos da aplicação, mas os seguintes pontos são geralmente razoáveis:

  1. Mantenha os logs de alterações agrupados por lançamentos de seu produto. Crie um novo diretório para cada versão e coloque os arquivos de changelog correspondentes nele. Ter um changelog raiz e incluir changelogs que correspondem aos lançamentos. Nos changelogs da versão, inclua outros changelogs que compõem esta versão.
  2. Tenha uma convenção de nomenclatura para arquivos de log de mudanças e identificadores de conjunto de alterações - e siga-a, é claro.
  3. Evite conjuntos de alterações com muitas alterações. Prefira vários changesets para um único changeset longo.
  4. Se você usar procedimentos armazenados e precisar atualizá-los, considere o uso de createTable atributo do conjunto de alterações no qual esse procedimento armazenado é adicionado. Caso contrário, cada vez que ele for atualizado, você precisará criar um novo conjunto de alterações com uma nova versão do procedimento armazenado. Os requisitos variam, mas muitas vezes é aceitável não rastrear esse histórico.
  5. Considere esmagar as alterações redundantes antes de mesclar os ramos do recurso. Às vezes, acontece que em um branch de recurso (especialmente em um de longa duração) conjuntos de alterações posteriores refinam as alterações feitas em conjuntos de alterações anteriores. Por exemplo, você pode criar uma tabela e depois decidir adicionar mais colunas a ela. Vale a pena adicionar essas colunas ao inicial context='test' mude se este branch de recurso não foi mesclado com o branch principal ainda.
  6. Use os mesmos registros de mudanças para criar um banco de dados de teste. Se você tentar fazer isso, logo descobrirá que nem todo conjunto de alterações é aplicável ao ambiente de teste ou que conjuntos de alterações adicionais são necessários para esse ambiente de teste específico. Com Liquibase, este problema é facilmente resolvido usando contextos . Basta adicionar o test para os conjuntos de alterações que precisam ser executados apenas com testes e, em seguida, inicializar o Liquibase com rollback contexto ativado.

Rolling Back

Como outras soluções semelhantes, Liquibase suporta a migração de esquema 'para cima' e 'para baixo'. Mas esteja avisado: desfazer migrações pode não ser fácil e nem sempre vale a pena o esforço. Se você decidiu oferecer suporte para desfazer migrações para seu aplicativo, seja consistente e faça isso para cada conjunto de alterações que precisaria ser desfeito. Com Liquibase, desfazer um changeset é realizado adicionando um createTable tag que contém as alterações necessárias para realizar uma reversão. Considere o seguinte exemplo:

addColumn

A reversão explícita é redundante aqui porque Liquibase executaria as mesmas ações de reversão. Liquibase é capaz de reverter automaticamente a maioria de seus tipos de alterações com suporte, por exemplo, createIndex, DATABASECHANGELOG ou DATABASECHANGELOG.

Consertando o Passado

Ninguém é perfeito e todos cometemos erros. Alguns deles podem ser descobertos tarde demais, quando as alterações danificadas já tiverem sido aplicadas. Vamos explorar o que pode ser feito para salvar o dia.

Atualizar manualmente o banco de dados

Envolve mexer com DATABASECHANGELOG e seu banco de dados das seguintes maneiras:

  1. Se você deseja corrigir conjuntos de alterações incorretos e executá-los novamente:
    • Remover linhas de MD5SUM que correspondem aos changesets.
    • Remova todos os efeitos colaterais introduzidos pelos changesets; por exemplo, restaure uma tabela se ela foi descartada.
    • Corrija os conjuntos de alterações ruins.
    • Execute as migrações novamente.
  2. Se você quiser corrigir conjuntos de alterações incorretos, mas pular a aplicação deles novamente:
    • Atualizar NULL definindo MD5SUM valor do campo para runOnChange='true' para aquelas linhas que correspondem aos conjuntos de alterações ruins.
    • Corrija manualmente o que foi feito de errado no banco de dados. Por exemplo, se houver uma coluna adicionada com o tipo errado, emita uma consulta para modificar seu tipo.
    • Corrija os conjuntos de alterações ruins.
    • Execute as migrações novamente. Liquibase irá calcular a nova soma de verificação e salvá-la em context. Os changesets corrigidos não serão executados novamente.

Obviamente, é fácil fazer esses truques durante o desenvolvimento, mas fica muito mais difícil se as alterações forem aplicadas a vários bancos de dados.

Escrever conjuntos de alterações corretivas

Na prática, essa abordagem geralmente é mais apropriada. Você pode se perguntar, por que não apenas editar o changeset original? A verdade é que depende do que precisa ser mudado. Liquibase calcula uma soma de verificação para cada conjunto de alterações e se recusa a aplicar novas alterações se a soma de verificação for nova para pelo menos um dos conjuntos de alterações aplicados anteriormente. Esse comportamento pode ser personalizado por conjunto de alterações, especificando runOnChange atributo. A soma de verificação não é afetada se você modificar pré-condições ou atributos de conjunto de alterações opcionais (context, context='graveyard-changesets-never-run', etc.).

Agora, você deve estar se perguntando, como eventualmente corrigir conjuntos de alterações com erros?

  1. Se você deseja que essas alterações ainda sejam aplicadas a novos esquemas, basta adicionar conjuntos de alterações corretivas. Por exemplo, se houver uma coluna adicionada com o tipo errado, modifique seu tipo no novo conjunto de alterações.
  2. Se você quiser fingir que esses changesets ruins nunca existiram, faça o seguinte:
    • Remova os changesets ou adicione changeSetExecuted com um valor que garante que você nunca tentará aplicar migrações com esse contexto novamente, por exemplo,
      .
    • Adicione novos conjuntos de alterações que irão reverter o que foi feito de errado ou consertar. Essas alterações devem ser aplicadas apenas se alterações incorretas foram aplicadas. Isso pode ser alcançado com pré-condições, como com
      |_+_|
      . Não se esqueça de adicionar um comentário explicando por que está fazendo isso.
    • Adicione novos conjuntos de alterações que modificam o esquema da maneira certa.

Como você vê, consertar o passado é possível, embora nem sempre seja simples.

Mitigando as dores de crescimento

Conforme seu aplicativo fica mais velho, seu log de mudanças também cresce, acumulando cada mudança de esquema ao longo do caminho. É intencional e não há nada inerentemente errado com isso. Logs de mudanças longos podem ser encurtados por meio do esmagamento regular das migrações, por exemplo, após o lançamento de cada versão do produto. Em alguns casos, tornaria a inicialização do esquema novo mais rápida.

ilustração de changelogs sendo esmagados

O esmagamento nem sempre é trivial e pode causar regressões sem trazer muitos benefícios. Outra ótima opção é usar um banco de dados de seed para evitar a execução de todos os changesets. É adequado para ambientes de teste se você precisar ter um banco de dados pronto o mais rápido possível, talvez até com alguns dados de teste. Você pode pensar nisso como uma forma de esmagar os conjuntos de alterações: em algum ponto (por exemplo, depois de lançar outra versão), você faz um despejo do esquema. Depois de restaurar o dump, você aplica as migrações normalmente. Apenas novas alterações serão aplicadas porque as mais antigas já foram aplicadas antes de fazer o despejo; portanto, eles foram restaurados do depósito.

ilustração de um banco de dados de sementes

Conclusão

Evitamos intencionalmente mergulhar mais fundo nos recursos do Liquibase para entregar um artigo que é curto e direto ao ponto, com foco na evolução dos esquemas em geral. Esperançosamente, está claro quais benefícios e problemas são causados ​​pela aplicação automatizada de migrações de esquema de banco de dados e como tudo se encaixa Cultura DevOps . É importante não transformar mesmo as boas ideias em dogmas. Os requisitos variam, e como engenheiros de banco de dados , nossas decisões devem estimular o avanço de um produto e não apenas seguir as recomendações de alguém na internet.

Compreender o básico

Qual é o significado de esquema no banco de dados?

O esquema do banco de dados descreve como os dados são organizados dentro do banco de dados.

Qual é a diferença entre um banco de dados e um esquema?

Um esquema é parte de um banco de dados. O banco de dados geralmente é composto de um ou mais esquemas. Além disso, é bastante comum chamar um DBMS de banco de dados. Normalmente fica claro no contexto se estamos falando sobre o contêiner de dados ou sobre o sistema que gerencia esse contêiner.

O que é um exemplo de esquema?

Se você estiver construindo um aplicativo de gerenciamento de turismo, seu esquema de banco de dados conteria entidades como companhia aérea, voo ou cidade. Além dos esquemas definidos pelo usuário, um SGBD geralmente possui um “esquema de informações” que pode ser usado para consultar as configurações e os metadados.

Quais são os diferentes tipos de bancos de dados?

Além dos bancos de dados relacionais, existem bancos de dados orientados a objetos, orientados a documentos e hierárquicos.

Existe uma maneira de tornar a consulta mais rápida?

Você pode otimizar uma consulta lenta reestruturando-a ou alterando o esquema. Um DBMS geralmente pode fornecer o plano de execução e ajudar a adivinhar o que está retardando sua consulta (por exemplo, não usar índices ou selecionar muitos dados em subconsultas). No PostgreSQL, você pode usar EXPLAIN ou EXPLAIN ANALYZE para entender o que está errado.

Qual é o esquema no PostgreSQL?

No PostgreSQL, você pode ter vários bancos de dados dentro do mesmo cluster; o esquema é um elemento estrutural do banco de dados. É o contêiner para tabelas, visualizações, procedimentos, etc. Os esquemas podem ser considerados diretórios dentro do banco de dados, mas os esquemas não podem conter outros esquemas.

Como fazer uma transmissão ao vivo no Instagram em 2021

Postagem

Como fazer uma transmissão ao vivo no Instagram em 2021
O Zen de devRant

O Zen de devRant

Estilo De Vida

Publicações Populares
Como organizar fotos no seu iPhone
Como organizar fotos no seu iPhone
Design do painel - Considerações e práticas recomendadas
Design do painel - Considerações e práticas recomendadas
‘Os EUA vão levantar diretamente com a China a questão do genocídio contra os muçulmanos uigures’
‘Os EUA vão levantar diretamente com a China a questão do genocídio contra os muçulmanos uigures’
Joe Biden corre o risco de perder o apoio dos democratas em meio a um impasse em DC
Joe Biden corre o risco de perder o apoio dos democratas em meio a um impasse em DC
Projete uma página inicial melhor com a estrutura StoryBrand
Projete uma página inicial melhor com a estrutura StoryBrand
 
HSA para desenvolvedores: computação heterogênea para as massas
HSA para desenvolvedores: computação heterogênea para as massas
Detido no aeroporto, o filho de Muhammad Ali perguntou 'Você é muçulmano?'
Detido no aeroporto, o filho de Muhammad Ali perguntou 'Você é muçulmano?'
Poluição do ar de Delhi: ‘é restritivo para as crianças, fique muito triste’, dizem os pais
Poluição do ar de Delhi: ‘é restritivo para as crianças, fique muito triste’, dizem os pais
Polícia mexicana captura capo do cartel 'La Tuta' Gomez
Polícia mexicana captura capo do cartel 'La Tuta' Gomez
Abandone MVPs, adote protótipos mínimos viáveis ​​(MVPr)
Abandone MVPs, adote protótipos mínimos viáveis ​​(MVPr)
Categorias
AprendendoÁfrica Do Oriente MédioProcesso InternoAméricasAscensão Do RemotoSaúdeMóvelFilmagemPostagemDesign De Iu

© 2023 | Todos Os Direitos Reservados

socialgekon.com