3.4. Transações

PostgreSQL 14.5: Transações

Transação é um conceito fundamental de todo sistema de banco de dados. O ponto essencial da transação é englobar vários passos em uma única operação de tudo ou nada. Os estados intermediários entre os passos não são vistos pelas demais transações simultâneas e, se ocorrer alguma falha que impeça a transação chegar até o fim, então nenhum dos passos intermediários irá afetar o banco de dados de forma alguma.

Por exemplo, considere um banco de dados de uma instituição financeira contendo o saldo da conta corrente de vários clientes, assim como o saldo total dos depósitos de cada agência. Suponha que se deseje transferir $100.00 da conta da Alice para a conta do Bob. Simplificando ao extremo, os comandos SQL para esta operação seriam:

UPDATE conta_corrente SET saldo = saldo - 100.00
    WHERE nome = 'Alice';
UPDATE filiais SET saldo = saldo - 100.00
    WHERE nome = (SELECT nome_filial FROM conta_corrente WHERE nome = 'Alice');
UPDATE conta_corrente SET saldo = saldo + 100.00
    WHERE nome = 'Bob';
UPDATE filiais SET saldo = saldo + 100.00
    WHERE nome = (SELECT nome_filial FROM conta_corrente WHERE nome = 'Bob');

Os detalhes destes comandos não são importantes aqui; o ponto importante é o fato de existirem várias atualizações distintas envolvidas para realizar esta operação tão simples. A contabilidade do banco quer ter certeza que todas estas atualizações foram realizadas, ou que nenhuma delas foi realizada. Com certeza não é interessante que uma falha no sistema faça com que Bob receba $100.00 que não foi debitado da Alice. Além disso, Alice não continuará sendo uma cliente satisfeita se o dinheiro for debitado de sua conta e não for creditado na conta do Bob. É necessário garantir que, caso aconteça algo errado no meio da operação, nenhum dos passos executados até este ponto irá valer. Agrupar as atualizações em uma transação dá esta garantia. Uma transação é dita como sendo atômica: do ponto de vista das outras transações, ou a transação acontece por inteiro, ou nada acontece.

Desejamos, também, ter a garantia de estando a transação completa e aceita pelo sistema de banco de dados que a mesma fique definitivamente gravada, e não seja perdida mesmo no caso de acontecer uma pane logo em seguida. Por exemplo, se estiver sendo registrado um saque em dinheiro pelo Bob não se deseja, de forma alguma, que o débito em sua conta corrente desapareça por causa de uma pane ocorrida logo depois do Bob sair da agência. Um banco de dados transacional garante que todas as atualizações realizadas por uma transação ficam registradas em meio de armazenamento permanente (ou seja, em disco), antes da transação ser considerada completa.

Outra propriedade importante dos bancos de dados transacionais está muito ligada à noção de atualizações atômicas: quando várias transações estão executando ao mesmo tempo, nenhuma delas deve enxergar as alterações incompletas efetuadas pelas outras. Por exemplo, se uma transação está ocupada totalizando o saldo de todas as agências, não pode ser visto o débito efetuado na agência da Alice mas ainda não creditado na agência do Bob, nem o contrário. Portanto, as transações devem ser tudo ou nada não apenas em termos do efeito permanente no banco de dados, mas também em termos de visibilidade durante o processamento. As atualizações feitas por uma transação em andamento não podem ser vistas pelas outras transações enquanto não terminar, quando todas as atualizações se tornam visíveis ao mesmo tempo.

No PostgreSQL a transação é definida envolvendo os comandos SQL da transação pelos comandos BEGIN e COMMIT. Sendo assim, a nossa transação bancária ficaria:

BEGIN;
UPDATE conta_corrente SET saldo = saldo - 100.00
    WHERE nome = 'Alice';
-- etc etc
COMMIT;

Se no meio da transação for decidido que esta não deve ser efetivada (talvez porque tenha sido visto que o saldo da Alice ficou negativo), pode ser usado o comando ROLLBACK em vez do COMMIT para fazer com que todas as atualizações sejam canceladas.

O PostgreSQL, na verdade, trata todo comando SQL como sendo executado dentro de uma transação. Se não for emitido o comando BEGIN, então cada comando possuirá um BEGIN implícito e, se der tudo certo, um COMMIT, envolvendo-o. Um grupo de comandos envolvidos por um BEGIN e um COMMIT é algumas vezes chamado de bloco de transação.

Nota: Algumas bibliotecas cliente emitem um comando BEGIN e um comando COMMIT automaticamente, fazendo com que se obtenha o efeito de um bloco de transação sem perguntar se isto é desejado. Verifique a documentação da interface utilizada.

É possível controlar os comandos na transação de uma forma mais granular utilizando os pontos de salvamento (savepoints). Os pontos de salvamento permitem descartar partes da transação seletivamente, e efetivar as demais partes. Após definir o ponto de salvamento através da instrução SAVEPOINT, é possível cancelar a transação até o ponto de salvamento, se for necessário, usando ROLLBACK TO. Todas as alterações no banco de dados efetuadas pela transação entre o estabelecimento do ponto de salvamento e o cancelamento são descartadas, mas as alterações efetuadas antes do ponto de salvamento são mantidas.

Após cancelar até o ponto de salvamento, este ponto de salvamento continua definido e, portanto, é possível cancelar várias vezes. Ao contrário, havendo certeza que não vai ser mais necessário cancelar até o ponto de salvamento, o ponto de salvamento poderá ser liberado, para que o sistema possa liberar alguns recursos. Deve-se ter em mente que liberar ou cancelar até um ponto de salvamento libera, automaticamente, todos os pontos de salvamento definidos após o mesmo.

Tudo isto acontece dentro do bloco de transação e, portanto, nada disso é visto pelas outras sessões do banco de dados. Quando o bloco de transação é efetivado, as ações efetivadas se tornam visíveis como uma unidade para as outras sessões, enquanto as ações desfeitas nunca se tornam visíveis.

Recordando o banco de dados da instituição financeira, suponha que tivesse sido debitado $100.00 da conta da Alice e creditado na conta do Bob, e descoberto em seguida que era para ser creditado na conta do Wally. Isso poderia ser feito utilizando um ponto de salvamento, conforme mostrado abaixo:

BEGIN;
UPDATE conta_corrente SET saldo = saldo - 100.00
    WHERE nome = 'Alice';
SAVEPOINT meu_ponto_de_salvamento;
UPDATE conta_corrente SET saldo = saldo + 100.00
    WHERE nome = 'Bob';
-- uai ... o certo é na conta do Wally
ROLLBACK TO meu_ponto_de_salvamento;
UPDATE conta_corrente SET saldo = saldo + 100.00
    WHERE nome = 'Wally';
COMMIT;

Obviamente este exemplo está simplificado ao extremo, mas é possível efetuar um grau elevado de controle sobre a transação através do uso de pontos de salvamento. Além disso, a instrução ROLLBACK TO é a única forma de obter novamente o controle sobre um bloco de transação colocado no estado interrompido pelo sistema devido a um erro, fora cancelar completamente e começar tudo de novo.

SourceForge.net Logo CSS válido!