MC536 - SQL: Transações Atômicas)Prof. Célio Guimarães IC - Unicamp |
Referência: livro-texto, pp 222-230.
Uma sequência de operações sobre uma Base de Dados que deve ser feita sem interrupções
é chamada de Transação Atômica. "Atomicidade" significa que "todas as
operações devem ser executadas ou nenhuma delas"
O exemplo simples a seguir ilustra o problema de inconsistência:
a partir de um caixa eletrônico um usuário faz uma transferência de valor
da sua conta poupança para sua conta corrente. As seguintes operações seriam
feitas pelo código sendo executado no SGBD:
A prevenção desses efeitos indesejáveis é um dos principais requisitos da
implementação do software de um SGBD, mesmo na presença das falhas itemizadas na
lista acima.
Deixando de lado,por enquanto, falhas catastróficas dos discos magnéticos, vamos
expor os princípio de uma técnica comum para garantir Atomicidade da transação.
Sob o ponto de vista do usuário ele usará comandos especiais para iniciar,
possivelmente abortar (por exemplo, se algum recurso necessário não
estiver disponível) ou concluir (commit) a transação. As operações que fazem parte da transação são comandos SQL entre o comando de início da transação e
o da sua a sua finalização:
Vários algoritmos e provas matemáticas da sua correção foram desenvolvidos na década de 70. Vamos apresentar um deles: ele se baseia no conceito de lock, que permite
a um processo (programa) obter acesso exclusivo a um objeto através de uma instrução
indivisível especial (ou seja se ela for executada simultaneamente por dois processos
um deles obtém o lock e o outro fica sabendo que não o obteve):
Uma possível otimização do algoritmo two-phase lock é conceder locks de leitura para transações que soment fazem leitura sobre um objeto: se T1 e T2 obtêm locks de leitura
sobre A elas podem proceder em paralelo sem bloqueio (o lock de leitura é necessário, porém, caso uma outra transação tente obter um lock de escrita sobre A).
A prevenção de todos esses efeitos pelo SGBD recebeu o mnemônico
ACID: Atomicidade, Consistência, Isolamento, Durabilidade.
1. leia saldo da poupança em X
2. X:= X - valor-digitado-pelo-usuário.
3. Escreve X no saldo da poupança.
4. Leia saldo da conta corrente em Y.
5. Y:= Y + valor-digitado-pelo-usuário
6. Escreva Y no saldo da conta corrente
Fica claro que esta sequência de operações, se executada completamente, leva o sistema
de um estado consistente para outro, porém se ela for interrompida após a
execução do passo 3 acima e antes da execuçõ do passo 6 a BD ficará
inconsistente: não somente some dinhero da poupança do nosso infeliz usuário,
como provavelmente, ele não poderá sacar o valor pretendido.
Mesmo que o sistema caia durante qualquer ponto dessas operações, quando ele
é re-iniciado um procedimento especial para recuperação das transações é executado, levando em conta a gravação na marca: se for transação iniciada tudo se passa como se a transação nunca tivesse sido iniciada e não há nada para recuperar; se for committed
as operações do log serão refeitas (pois podem não ter sido concluídas)
e se for finished também não há nada a fazer.
Na prática o sistema é bem mais compĺexo, porque transações de outros usuários podem estar concorrentemente acessando as mesmas tabelas e sendo registradas no log.
Isto requer teorias e técnicas novas a serem vistas mais adiante.
exec sql begin transaction
comandos SQL de leitura/escrita de dados na BD
. . .
exec sql commit (ou exec sql rollback)
Transações Concorrentes
Transações que atualizam "simultanemente" o mesmo dado na BD, podem gerar inconsistências como por exemplo, dois agentes de viagem resevarem o mesmo assento de um vôo.
Como uma transação atômica sempre leva a BD de um estado consistente para outro,
a execução serial (uma após a outra) de n transações atômicas deixará a BD num estado consistente, embora possivelmente diferente, dependendo da ordem de execução pois o resultado de uma transação poderia ser usado pela seguinte.
Esta solução é inaceitável por várias razões:
Um dado escalonamento de n transações se diz serializável se
o efeito final sobre a BD seria o mesmo efeito produzido por alguma execução
serial dessas transações.
T2 não conseguiu o assento desejado, mas uma reserva inaceitável do mesmo assento
para dois passageirosn não seria feita (aconteceu uma vez comigo!)
na fase 1 a transação procura obter todos os locks para tabelas de que precisa
(ou de linhas de tabelas) Se não conseguir algum (por ter sido concedido antes para outra transação) ela fica bloqueada.
Na fase 2 a transação que conseguiu todos os locks, e somente então, executa
o commit: o sistema faz as atualizações requeridas na BD,
e libera os locks da transação, permitindo outras transações bloqueadas continuarem.
O algoritmo tem uma falha crucial: permite que duas transações T1 e T2 se
bloqueiem indefinidamente caso elas acessem dois objetos da BD, digamos, A e B
em ordem distinta na seguinte ordem temporal:
T1 obtem lock sobre A
T2 obtem lock sobre B
T1 pede lock sobre B
(T1 fica bloqueada) T2 pede lock sobre A
(T2 fica bloqueada)
Nesse caso diz-se que T1 e T2 entraram em deadlocK.
Cadeias com mais de duas transações em mutuo deadlock são possiveis.
É fundamental, portanto, que o SGDB implemente:
(i) um mecanismo de detecção de deadlocks,
(ii) uma política de quebra de deadlocks, abortando as transações com deadlocks
quebrados e re-iniciando-as posteriormente.
Níveis de isolação de transações em SQL/92
Serializibilidade é a política padrão de contrôle de concorrência em SQL.
Há aplicações que ficam mais ágeis com políticas menos rígidas, relaxando
a exigência de serializibilidade. Elas são chamadas de "niveis de isolamento"
e são aplicadas através dos seguintes comandos imediatamente antes
do início da transação (no exemplo supomos que T1 tem um lock sobre um objeto A e faz
múltiplas atualizações sobre A; T2 obtem o lock especial sobre A e faz múltiplas
leituras de A):
nesse caso T2 deve sempre ler o mesmo valor para A.
nesse caso T2 poderia ler valores diferentes de A desde que eles tenham sido
escritos por transações (distintas) que terminaram com sucesso.
nesse caso T2 lê um valor que T1 eventualmente não gravou na BD porque
abortou a transação, ou seja, T2 lê um valor que nunca existiu de fato na BD!
Por incrível que pareça pode ser útil neste exemplo:
T1 (um agente de viagens) reserva o assento A no vôo Y.
T2 (outro agente de viagens) tenta reservar o assento A no vôo Y mas não consegue
o cliente de T1 cancela o vôo e T1 libera o assento A
Consistência em Sistemas Distribuídos NoSQL
Três requisitos desejáveis não podem ser obtidos simultâneamente:
"We can only achieve at most two out of three guarantees for a database:
Consistency, Availability, and Partition Tolerance".