Arquitetura Multi-Geracional (Multigenerational Architecture) resumida


Não existem documentos sobre como a MGA (Multigenerational Architecture) funciona. Neste documento tentaremos descreve-la da forma mais simples.
 
MGA em duas palavras
Multi-Geracional significa que podem existir muitas versões de cada registro na base de dados. As versões são criadas pelo engine da seguinte maneira:

Fig. 1
Imagine que exista uma tabela com vários registros, e que existe uma transação TR1 lendo um registro.

Fig. 2

Neste momento entra em jogo uma outra transação TR2. Ela pode (e vai) ler o mesmo registro. Mas, se a TR2 modificar este registro com uma sentença de UPDATE,
Fig. 3
o engine criará uma nova versão deste registro e o armazenará próximo ao registro original.

Fig. 4

Uma vez que o cabeçalho de todos os registros e de suas versões possuem informação sobre a transação que o criou, a visibilidade das versões são determinadas comparando os números das transações (a que lê com a que criou o registro).


Simples assim.

nota: novas versões de registros são criadas como “deltas”, i.e. a diferença entre o registro antigo e o novo. Este é o motivo pelo qual armazenar versões é muito eficiente e na maioria dos casos menos do que o dobro do tamanho do registro original.
 
Histórico da MGA
Claro que não é tão simples assim. Na verdade é um pouco mais complexo. Vamos nos aprofundar nos detalhes.

Estado das transações

Quando o servidor recebe um comando para iniciar uma transação ele faz o seguinte:
  • Gera um novo número para a transação. É um simples contador auto-incrementado; você pode ver o número do contador “next transaction” no relatório de estatísticas do banco de dados (use gstat –h ou IBStat para visualizar).
  • Aloca 2 bits na Transaction Inventory Page (TIP) – um tipo especial de página do banco de dados usado para armazenar os estados das transações.

Todos os registros ou versões de registros criados por esta transação será marcado por este número. Outras transações verificarão o estado na TIP quando lerem estas versões.

Existem quatro estados para uma transação (dois bits = quatro estados):

  1. Active (Ativa) – transações ainda em execução
  2. Committed – transação finalizada e “comitada”
  3. Rolled back – transação finalizada mas desfeita (rollback)
  4. In Limbo (No limbo) – Preparada, mas aguardando um Commit

O quarto estado só pode ocorrer durante um Commit em Duas Fases (Two Phase Commit) que ainda não foi finalizado. Neste artigo não falaremos sobre Commit em duas fases devido a ser uma funcionalidade raramente usada.

Visibilidade das versões
Como foi dito na primeira parte a visibilidade dos registros ou de suas versões são determinadas pela leitura do número da transação no cabeçalho do registro. Isto parece complicado, mas na verdade é algo bem simples. Por exemplo, se a transação está ativa ela pode visualizar, ao menos, todas as versões criadas por ela mesma (marcadas com o número 500) e todas as versões criadas por transações anteriores (com números menores que 500) que já foram comitadas (estado “committed”na TIP).
 
Read Committed e Snapshot
InterBase/Firebird possuem apenas dois tipos principais de transações (níveis de isolamento) - ReadCommitted e Snapshot. Veja a diferença:

  • Transações ReadCommitted olham no TIP global para verificar os estados das transações concorrentes.
  • Transações Snapshot fazem uma cópia local do TIP global quando iniciam, e usam apenas esta cópia local para verificar o estado das transações concorrentes

Portando transações ReadCommitted podem ver a mudança de estado de qualquer transação subsequente. Como no exemplo acima, se a versão do registro foi criada por uma transação com um número maior que 500 e está comitada então a transação 500 poderá visualizar esta versão do registro.

Transações snapshot não podem ver novas transações, ou mudança de estado das transações, uma vez que usa a cópia local do TIP que não é atualizada pelo engine. Este é o motivo pelo qual se uma transação ler a versão do registro criada pela transação 520 que não está na cópia local do TIP, portanto seu estado não pode ser determinado e a versão não poderá ser vista.

Nota: "versão pode ser vista pela transação" na verdade quer dizer que o engine pode enviar esta versão para o cliente como um registro real. Claro que cada transação independente do seu nível de isolamento pode ver todas as versões de todos os registros.

Agora podemos criar a fórmula de “visibilidade de versões”:

Uma versão do registro estará visivel se:
(tr_id = número da transação da versão do registro que está sendo lida)

  • tr_id = própria transação (i.e. a própria transação criou esta versão)
  • tudo onde tr_id < própria transação, que estejam comitadas (para qualquer tipo de transação)
  • tudo onde tr_id > própria transação, que estejam comitadas, se a transação for read_committed
 
Quantas versões ?
De quantas você gostaria ? Cada atualização de um registro gera uma nova versão de registro. Então podem existir muitas versões comitadas para um registro (nós já vimos por volta de 1.500.000 versões de um registro em uma base de dados). Mas só pode existir uma versão de registro não comitada.

Se duas transações tentarem atualizar o mesmo registro, a primeira terá sucesso, mas a segunda terá uma mensagem de erro de deadlock. Este é o único caso em que transações são conflitantes no InterBase/Firebird.

Commit, Rollback

Em alguns casos o servidor pode mudar o estado da transação por conta própria, mesmo que a transação foi finalizada corretamente ou se a conexão entre servidor e cliente for interrompida.

  1. Commit nunca é convertido para nada. Isto é fato.
  2. Rollback pode ser convertido para um Commit, se:
    1. Não houve nenhuma mudança de dados na transação
    2. Todas as mudanças na transação foram canceladas por uma exceção (erro ou chamada explicita de exceção)
    3. Não sejam muitas alterações durante a transação("Não sejam muitas" foi aumentado na versão 7.1 SP 1 do InterBase, e agora quer dizer 100 mil registros atualizados), no Firebird e versões antigas do Interbase é por volta de 60 mil.
  3. O estado Active da transação será convertido para Rollback (ou Commit, vejam item 2), se o servidor achar que a conexão da transação foi interrompida.

Vale mencionar o novo comportamento para transações "read only read committed" – a partir do InterBase 6.0 (em todas as versões do Firebird e Interbase posteriores) tais transações são iniciadas com o estado como “committed”, ao invés de “active”, e podem durar para sempre sem afetar a performance do servidor, processo de garbage collection, etc.
 
Arrumação da casa de forma cooperativa
Você pode estar pensando agora que se as versões são criadas de forma constante então o banco de dados irá crescer de forma constante até que você não poderá trabalhar com este banco de dados.

Claro que o servidor possui mecanismos para eliminar versões de registros desnecessárias. Este mecanismo é chamado de "cooperative garbage collection" (coleta de lixo cooperativa).

O mecanismo funciona como “se alguém quer comer, precisa lavar os pratos”.

Quando o servidor lê um registro, como dito anteriormente, ele lê um conjunto de registros e suas versões (uma vez que a base de dados é lida por páginas e não por registros, e na maioria dos casos os registros e suas versões estão na mesma página). Agora o servidor poderá executar duas tarefas principais:

  1. Determinar quais versões estão visíveis para a transação atual e enviar a versão completa ao cliente
  2. Encontrar versões que não são mais necessárias para nenhuma transação e elimina-las.

A primeira tarefa é simples e já falamos sobre ela. A parte complicada da história é que o registro enviado para o cliente é o resultado de uma combinação do registro original e todas as versões visíveis (devido ao fato de que as versões são apenas deltas). Esta é a razão pela qual quanto mais versões mais lento será o retorno de registros pelo servidor (mas esperamos que não proporcionalmente a quantidade de versões).

A próxima tarefa é resolvida usando um outro caminho. O primeiro passo (ou passo zero se preferir) o servidor armazena não apenas o número da next transaction, mas todos os números necessários para dar suporte a TIP.

Que são:

  • Oldest Active Transaction (OAT) – O menor número de transação que ainda está ativa.
  • Oldest Interesting Transaction (OIT) – O menor número de transação que não está comitada. Este pode ser o OAT ou o número da transação mais antiga que esteja em “Rollback” ou “In Limbo”.
  • Oldest Snapshot Transaction (OST) – é um pouco mais complexo do que simplesmente o número do snapshot:
    • "read only read committed" não tem este número
    • "read write read committed" possuem OST igual ao seu próprio número
    • snapshots” possuem OST igual a OAT

Se uma transação encontra versões que estão comitadas e possuem transação menor do que OST, o engine pode remover esta versão. Isto não é feito sobre dados existentes e uma queda do sistema não vai gerar perda de versão..

Além disso – Cooperação também é usada para encontrar transações de conexões interrompidas – Seu estado é convertido de active somente quando outra transação lê versões e lê a TIP para determinar o estado da transação. Como você pode ver, o servidor não força um processo de garbage collection nem detecta conexões interrompidas.
 
Resumo
Isto é tudo o que pode ser dito sobre a MGA de forma sucinta. Esperamos que você tenha entendido tudo desde o início. Se você quiser se aprofundar mais – leia os documentos relacionados.

Ficou com alguma dúvida, pergunte-nos (support@ib-aid.com )


Links


No comments:

Post a Comment