Documentação do PostgreSQL 8.0.0 | ||||
---|---|---|---|---|
Anterior | Início | Capítulo 12. Controle de simultaneidade | Fim | Próxima |
Como no PostgreSQL a leitura não bloqueia os dados, independentemente do nível de isolamento da transação, os dados lidos por uma transação podem ser sobrescritos por outra transação simultânea. Em outras palavras, se uma linha foi retornada pelo comando SELECT, não significa que esta linha ainda era a linha corrente no instante em que foi retornada (ou seja, algum tempo depois do comando corrente ter começado). A linha pode ter sido alterada ou excluída por uma transação já efetivada, que efetivou após esta transação ter começado. Mesmo que a linha ainda seja válida "agora", esta linha pode ser mudada ou excluída antes da transação corrente efetivar ou desfazer suas alterações.
Outra maneira de pensar sobre isto é que cada transação enxerga um instantâneo do conteúdo do banco de dados, e as transações executando simultaneamente podem, perfeitamente bem, enxergar instantâneos diferentes. Portanto, o próprio conceito de "agora" de alguma forma é mal definido. Normalmente isto não é um grande problema quando os aplicativos cliente estão isolados um do outro, mas se os clientes puderem se comunicar por meio de canais externos ao banco de dados, então podem acontecer sérias confusões.
Para garantir a validade corrente de uma linha e protegê-la contra atualizações simultâneas, deve ser utilizado o comando SELECT FOR UPDATE ou uma declaração LOCK TABLE apropriada (o comando SELECT FOR UPDATE bloqueia apenas as linhas retornadas contra atualizações simultâneas, enquanto o LOCK TABLE bloqueia toda a tabela). Isto deve ser levado em consideração ao portar aplicativos de outros ambientes para o PostgreSQL (Antes da versão 6.5 o PostgreSQL utilizava bloqueios de leitura e, portanto, as considerações acima também se aplicam quando é feita a atualização de uma versão do PostgreSQL anterior a 6.5).
As verificações de validade globais requerem considerações adicionais sob o MVCC. Por exemplo, um aplicativo bancário pode desejar verificar se a soma de todos os créditos em uma tabela é igual a soma de todos os débitos em outra tabela, num momento em que as duas tabelas estão sendo ativamente atualizadas. Comparar os resultados de dois comandos SELECT SUM(...) sucessivos não funciona confiavelmente no modo Read Committed, porque o segundo comando, provavelmente, vai incluir resultados de transações não contabilizadas pelo primeiro comando. Realizar as duas somas em uma mesma transação serializável fornece uma imagem precisa dos efeitos das transações efetivadas antes do início da transação serializável — mas pode ser legitimamente questionado se a resposta ainda era relevante na hora que foi entregue. Se a própria transação serializável introduziu algumas alterações antes de tentar efetuar a verificação de consistência, o valor prático da verificação se torna ainda mais discutível, porque agora são incluídas algumas, mas não todas as alterações ocorridas após o início da transação. Em casos como este, uma pessoa cuidadosa pode desejar bloquear todas as tabelas necessárias para a verificação, para obter uma imagem da situação atual acima de qualquer suspeita. Um bloqueio no modo SHARE (ou superior) garante não haver alterações não efetivadas na tabela bloqueada, fora as alterações efetuadas pela própria transação corrente.
Deve ser observado, também, que quando se depende de bloqueios explícitos para evitar alterações simultâneas deve ser utilizado o modo Read Committed ou, no modo serializável, tomar o cuidado de obter os bloqueios antes de executar os comandos. Um bloqueio obtido por uma transação serializável garante que nenhuma outra transação alterando a tabela está executando, mas se o instantâneo enxergado pela transação for anterior à obtenção do bloqueio, pode ser que seja anterior a algumas alterações na tabela que agora estão efetivadas. O instantâneo de uma transação serializável é, na verdade, tirado no início da sua primeira consulta ou comando de alteração de dados (SELECT, INSERT, UPDATE ou DELETE) sendo, portanto, possível obter bloqueios explicitamente antes do instantâneo ser tirado.