Implantação de tempo de inatividade zero - esquema de banco de dados transitório

14

Atingir Zero Downtime Deployment abordou o mesmo problema, mas preciso de alguns conselhos sobre uma estratégia que estou considerando.

Contexto

Um aplicativo baseado na Web com Apache / PHP para processamento no servidor e sistema de arquivos / banco de dados MySQL para persistência.

Atualmente, estamos construindo a infraestrutura. Todo o hardware de rede terá redundância e todos os cabos principais da rede serão usados ​​em pares ligados para tolerância a falhas. Os servidores estão sendo configurados como pares de alta disponibilidade para tolerância a falhas de hardware e terão balanceamento de carga para tolerância a falhas de máquina virtual e desempenho geral.

É minha intenção que possamos aplicar atualizações ao aplicativo sem nenhum tempo de inatividade. Fiz um grande esforço ao projetar a infraestrutura para garantir que eu possa fornecer 100% de tempo de atividade; seria extremamente decepcionante ter 10 a 15 minutos de inatividade toda vez que uma atualização fosse aplicada. Isso é particularmente significativo, pois pretendemos ter um ciclo de lançamento muito rápido (às vezes, pode atingir um ou mais lançamentos por dia.

Topologia de rede

Este é um resumo da rede:

                      Load Balancer
             |----------------------------|
              /       /         \       \  
             /       /           \       \ 
 | Web Server |  DB Server | Web Server |  DB Server |
 |-------------------------|-------------------------|
 |   Host-1   |   Host-2   |   Host-1   |   Host-2   |
 |-------------------------|-------------------------|
            Node A        \ /        Node B
              |            /            |
              |           / \           |
   |---------------------|   |---------------------|
           Switch 1                  Switch 2

   And onward to VRRP enabled routers and the internet

Nota: os servidores de banco de dados usam replicação master-master

Estratégia sugerida

Para conseguir isso, atualmente estou pensando em dividir os scripts de atualização do esquema do DB em duas partes. A atualização seria assim:

  1. O servidor da Web no nó A é retirado da rede; o tráfego continua sendo processado pelo servidor da web no nó B.
  2. Alterações de esquema de transição são aplicadas aos servidores de banco de dados
  3. Servidor da Web Uma base de código é atualizada, os caches são limpos e quaisquer outras ações de atualização são executadas.
  4. O servidor Web A é colocado online e o servidor B é colocado offline.
  5. A base de código do servidor Web B é atualizada, os caches são limpos e quaisquer outras ações de atualização são executadas.
  6. O servidor da Web B é colocado online.
  7. Alterações finais do esquema são aplicadas ao banco de dados

'Esquema de transição' seria projetado para estabelecer um banco de dados compatível com a versão cruzada. Isso usaria principalmente visualizações de tabela que simulam o esquema da versão antiga, enquanto a própria tabela seria alterada para o novo esquema. Isso permite que a versão antiga interaja com o banco de dados normalmente. Os nomes das tabelas incluiriam números de versão do esquema para garantir que não haja confusão sobre em qual tabela gravar.

'Esquema final' removeria a compatibilidade com versões anteriores e arrumaria o esquema.

Questão

Em suma, isso vai funcionar?

mais especificamente:

  1. Haverá problemas devido ao potencial de gravações simultâneas no ponto específico da mudança de esquema de transição? Existe uma maneira de garantir que o grupo de consultas que modifica a tabela e cria a exibição compatível com versões anteriores seja executado consecutivamente? ou seja, com outras consultas sendo mantidas no buffer até que as alterações no esquema sejam concluídas, o que geralmente será de apenas milissegundos.

  2. Existem métodos mais simples que fornecem esse grau de estabilidade e também permitem atualizações sem tempo de inatividade? Também é preferível evitar a estratégia de esquema "evolucionária", pois não desejo ficar preso à compatibilidade de esquemas com versões anteriores.

Marvin
fonte

Respostas:

4

Parece que o que você está realmente procurando não é tanto a alta disponibilidade quanto a necessidade da disponibilidade contínua .

Essencialmente, seu plano funcionará, mas você parece ter notado que a principal falha em sua configuração é que as alterações no esquema do banco de dados em uma liberação podem resultar em tempo de inatividade ou falha do funcionamento do nó ainda disponível. A abordagem de disponibilidade contínua resolve isso criando essencialmente vários ambientes de produção.

Produção Um

Esse ambiente é a sua versão ao vivo atual do software que está sendo utilizada pelos usuários. Possui seus próprios servidores da Web, servidores de aplicativos e servidores de banco de dados e espaço de tabela. Opera independentemente de qualquer outro ambiente. O Load Balancer que possui o ponto de extremidade de resolução de domínio para esses serviços está atualmente apontando para esses servidores da web.

Produção Dois

Este é basicamente o ambiente de preparação da versão idêntico ao Production One. Você pode realizar suas atualizações de versão aqui e fazer seus testes de sanidade antes do evento ao vivo. Isso também permite que você execute com segurança suas alterações no banco de dados nesse ambiente. O Load Balancer não aponta para este ambiente atualmente.

DR de produção

Essa é outra duplicata em um data center separado, localizado em uma região diferente do mundo. Isso permite que você faça failover em caso de evento catastrófico executando uma opção DNS no Load Balancer.

Go Live

Este evento está essencialmente atualizando o registro DNS para passar para a Produção Dois da Produção Um ou vice-versa. Demora um pouco para se propagar pelos servidores DNS do mundo, para que você deixe os dois ambientes em execução por um tempo. Alguns usuários podem estar trabalhando em sessões existentes na versão antiga do seu software. A maioria dos usuários estabelecerá novas sessões na versão atualizada do seu software.

Migração de dados

A única desvantagem aqui é que nem todos os dados durante essa janela estão disponíveis para todos os usuários naquele momento. Há claramente dados importantes do usuário no banco de dados da versão anterior que agora precisam ser migrados com segurança para o novo esquema do banco de dados. Isso pode ser realizado com um script de exportação e migração de dados bem testado ou trabalho em lote ou processo ETL semelhante.

Conclusão

Depois de concluir completamente o evento de lançamento, a Produção Dois agora é sua principal e você começa a trabalhar na instalação do próximo lançamento no Production One para o próximo ciclo de implantação.

Desvantagens

Essa é uma configuração de ambiente complexa e requer uma grande quantidade de recursos do sistema, geralmente duas a três vezes os recursos do sistema para executar com êxito. Operar dessa maneira pode ser caro, especialmente se você tiver sistemas de uso pesado muito grandes.

maple_shaft
fonte
Portanto, se eu entendi corretamente, você sugere que, em vez de uma mudança de esquema de banco de dados 'transitória' aplicada enquanto o Db ainda está em uso, o Db-A é mantido online com o esquema antigo, enquanto o Db-B é atualizado para o novo esquema. Quando a atualização está pronta para lançamento, os servidores da Web são alterados e os dados que foram gravados no Db A enquanto a atualização estava sendo preparada são migrados para o Db B (presumivelmente, obtendo todas as alterações aplicadas após um carimbo de data / hora específico).
Marvin
@PeterScott Você conseguiu. Lembre-se de que não deseja executar o script até ter certeza de que todas as sessões ativas terminaram no sistema antigo e já faz tempo que todos os caches DNS foram atualizados para o novo endereço CNAME ou IP.
maple_shaft
1
Eu deveria estar bem em ambos os pontos; as sessões estão sendo mantidas no banco de dados em vez do armazenamento do servidor para evitar que as sessões sejam vinculadas a máquinas virtuais específicas e, atualmente, pretendo tentar usar um balanceador de carga não baseado em DNS. Não terei redundância no nível do data center, mas isso pode esperar um ano ou mais após o lançamento do aplicativo.
Marvin
2

Sua estratégia é sólida. Eu ofereceria apenas considerar a possibilidade de expandir o "Esquema de transição" em um conjunto completo de "tabelas de transações".

Com tabelas de transações, os SELECTs (consultas) são executados em relação às tabelas normalizadas para garantir a correção. Mas todos os INSERTs, UPDATEs e DELETEs do banco de dados são sempre gravados nas tabelas de transações desnormalizadas.

Em seguida, um processo simultâneo separado aplica essas alterações (talvez usando Procedimentos armazenados) às tabelas normalizadas de acordo com as regras de negócios e os requisitos de esquema estabelecidos.

Na maioria das vezes, isso seria praticamente instantâneo. Mas a separação das ações permite que o sistema acomode atrasos excessivos de atividade e atualização de esquema.

Durante as alterações de esquema no banco de dados (B), as atualizações de dados no banco de dados ativo (A) entrariam em suas tabelas de transações e seriam imediatamente aplicadas a suas tabelas normalizadas.

Ao recuperar o banco de dados (B), as transações de (A) seriam aplicadas a ele, gravando-as nas tabelas de transação de (B). Depois que essa parte estiver concluída, (A) poderá ser desativado e o esquema será alterado. (B) terminaria de aplicar as transações de (A) e, ao mesmo tempo, manipularia suas transações ativas, que seriam colocadas na fila exatamente como (A) e as "operações ativas" seriam aplicadas da mesma maneira quando (A) retornasse.

Uma linha da tabela de transações pode parecer algo como ...

| ROWID | TRANSNR | DB | TABLE | SQL STATEMENT
    0        0       A    Name   INSERT INTO Name ...
    1        0       A    Addr   INSERT INTO Addr ...
    2        0       A    Phone  INSERT INTO Phone ...
    3        1       A    Stats   UPDATE Stats SET NrOfUsers=...

As "tabelas" da transação podem realmente ser linhas em um banco de dados NoSQL separado ou até arquivos seqüenciais, dependendo dos requisitos de desempenho. Um bônus é que a codificação do aplicativo (site, neste caso) fica um pouco mais simples, pois grava apenas nas tabelas de transações.

A idéia segue os mesmos princípios da contabilidade de dupla entrada e por razões semelhantes.

As tabelas de transações são análogas a um "diário" da contabilidade. As tabelas totalmente normalizadas são análogas a um "razão" de contabilidade, sendo que cada tabela é um pouco como uma "conta" de contabilidade.

Na contabilidade, cada transação recebe duas entradas no diário. Um para a conta contábil "debitada" e outro para a conta "creditada".

Em um RDBMS, um "diário" (tabela de transações) obtém uma entrada para cada tabela normalizada a ser alterada por essa transação.

A coluna DB na ilustração da tabela acima indica em qual banco de dados a transação se originou, permitindo que as linhas enfileiradas do outro banco de dados sejam filtradas e não reaplicadas quando o segundo banco de dados for recuperado.

DocSalvager
fonte
1
Gosto da comparação com a contabilidade. Portanto, se eu entendi, as tabelas de transações permitem um atraso muito pequeno na gravação de dados em uma tabela normalizada específica, para que eu possa aplicar todas as alterações de esquema sem risco de interrupção no meio das alterações. Em seguida, com o esquema da tabela atualizado, posso retomar o processo que aplica as transações desnormalizadas às tabelas normalizadas (esse processo é capaz de mapear as consultas de dados do esquema antigo para o novo esquema)?
Marvin
1
Sim. Você modificaria os procedimentos armazenados de mapeamento (ou qualquer outro) para acomodar dados antigos e novos. Novas colunas NOT-NULL podem ser preenchidas a partir de dados antigos com um código que significa "solicitar isso na atualização do usuário". As colunas a serem divididas (ou seja, FULLNAME em PRIMEIRO e ÚLTIMO) precisariam de algum algoritmo. Eu recomendo adicionar 1 ou mais colunas "tipo comentário" às tabelas para novos requisitos de negócios que surgirem. Caso contrário, garanto que os usuários se apropriarão de outras colunas para esse fim e a correção dos dados será quase impossível.
DocSalvager 29/01
Como você evitaria que consultas SELECT estruturadas para o esquema antigo fossem aplicadas ao novo esquema? Eu poderia usar criar uma exibição de tabela e renomear a tabela de esquema (com um número de versão do esquema), mas isso ainda seria problemático enquanto as alterações de esquema estão sendo aplicadas, uma vez que se aplicam diretamente à tabela normalizada.
Marvin
1
Quando você adiciona uma tabela, coluna ou qualquer outra coisa a um RDBMS, na verdade você está apenas adicionando linhas a um conjunto de tabelas internas que podem ser gravadas apenas pelo mecanismo RDBMS. Os DBAs gerenciam o banco de dados consultando-os através de VIEWs. Como Oracle, IBM, MS, etc. são os especialistas e dizem que esse é o melhor caminho, parece que devemos seguir a liderança deles. Crie um conjunto de visualizações para cada versão do aplicativo. Você pode modelá-los após as tabelas (geralmente desnormalizadas) que os desenvolvedores desejam que você crie, para que você possa normalizar adequadamente para evitar dados corrompidos.
DocSalvager 29/01
Obrigado. Vou precisar pensar sobre isso. Estou construindo uma camada ORM no aplicativo que remove completamente toda a lógica de persistência de estado do domínio principal; sendo mais baseado na programação do lado do servidor, tendem a resolver problemas mais desse lado do que do lado da administração do banco de dados. Usando minha estratégia atual, o banco de dados seria bastante plano com o ORM gerenciando ativamente as tabelas brutas. A adição de visualizações de tabela e, possivelmente, um log de transações adiciona maior complexidade ao ORM, mas também permite que várias versões de esquema sejam suportadas sem fragmentação de dados.
Marvin