Você está na página 1de 19

Aula 11: Preparação AV2

Por que o controle de concorrência é necessário?

1. Problema de atualização perdida


2. Problema de leitura suja
3. Problema do resumo incorreto
4. Problema da leitura não repetitiva: quando uma
transação lê o item duas vezes e, entre essas duas leituras, uma outra
transação modifica o item. Ou seja, serão duas leituras diferentes para
o mesmo item.
Controle da Concorrência
O sistema precisa registrar quando cada transação começa, quando termina e se confirma ou se
cancela (aborta).

▪ COMMIT: finaliza a transação atual tornando permanentes todas as alterações de dados pendentes.

▪ SAVEPOINT <nome_savepoint>: marca um ponto de gravação dentro da transação atual, sendo utilizado para dividir uma
transação em partes menores.

▪ ROLLBACK [TO SAVEPOINT <nome_savepoint>]:


▪ ROLLBACK finaliza a transação atual, descartando todas as alterações de dados pendentes.

▪ ROLLBACK TO SAVEPOINT descarta o ponto de gravação determinado e as alterações seguintes ao mesmo.


Propriedades de uma Transação (ACID)

❑ Atomicidade: uma transação é uma unidade atômica de processamento; é realizada

integralmente ou não é realizada.

❑ Consistência: uma transação é consistente se levar o banco de dados de um estado

consistente para outro estado também consistente.

❑ Isolamento: a execução de uma transação não deve sofrer interferência de

quaisquer outras transações que estejam sendo executadas concorrentemente.

❑ Durabilidade (ou persistência): as alterações aplicadas ao banco de dados, por meio

de uma transação confirmada, devem persistir no banco de dados, não sendo

perdidas por alguma falha.


Processo de Recuperação (recovery)
Exemplo:

1. Algoritmo é iniciado com duas listas


(Undo e Redo). UNDO (desfazer) com a
lista das transações no momento do
último checkpoint e REDO (refazer)
vazia;
1. UNDO: <T2; T3>
2. Pesquisa-se no log de transações a 2. Início do LOOP
partir do registro de checkpoint; 3. T2 vai para REDO (COMMIT encontrado)

3. Se um Begin Transaction for 4. T4 vai para UNDO (BEGIN TRANSACTION encontrado)


5. T5 vai para UNDO (BEGIN TRANSACTION encontrado)
encontrado, adicionamos a transação à
6. T4 vai para REDO (COMMIT encontrado)
lista UNDO;
7. FIM DO LOOP
4. Se um commit for encontrado, move a Portanto:
transação da lista UNDO para a REDO; UNDO: <T3; T5> “Serão desfeitas” “forward recovery”
REDO: <T2; T4> “Serão refeitas” “backward recovery”
T1: nada a fazer
Schedules (históricos)
Exemplo:

Duas operações em um schedule estão Sa = r1(X); r2(X); w1(X); r1(Y); w2(X); w1(Y)

em conflito se forem satisfeitas as Conflitos:

seguintes condições: i) r1(X) e w2(X)

1. Elas pertencem a diferentes transações. ii) r2(X) e w1(X)

2. Elas acessam o mesmo item X. iii) w1(X) e w2(X);

3. Pelo menos uma delas é um


Não estão em conflito:

write_item(X). r1(X) e r2(X); “ambas leituras”

w2(X); w1(Y); “elementos diferentes”

r1(X) e w1(X); “mesma transação”


Serialização de Schedules
Algoritmo: testando a serialização de conflito de S Exemplo: escalonamento d
Transação T1 Transação T2
1. Para cada transação Ti de S, crie um nó rotulado com
read_item(X);
Ti no grafo de precedência.
X := X – N;
2. Para cada caso em S onde Tj executa um
write_item(X);
read_item(X) depois de Ti executar um
read_item(X);
write_item(X), crie uma aresta (Ti → Tj) no grafo de X := X + M;
precedência. write_item(X);

3. Para cada caso S onde Tj executa um write_item(X) read_item(Y);

após Ti executar um read_item(X), crie uma aresta Y := Y + N;


write_item(Y);
(Ti → Tj) no grafo de precedência.

Para cada caso S onde Tj executa um write_item(X)


4.
T2
após Ti executar um write_item(X), crie uma aresta
(Ti → Tj) no grafo de precedência. T1
X
5. O schedule é serializável se, e somente se, o grafo de
precedência não tiver ciclos. d é serializável
Serialização de Schedules
Algoritmo: testando a serialização de conflito de S Exemplo: escalonamento c
Transação T1 Transação T2
1. Para cada transação Ti de S, crie um nó rotulado com
read_item(X);
Ti no grafo de precedência.
X := X – N;
2. Para cada caso em S onde Tj executa um
read_item(X);
read_item(X) depois de Ti executar um X := X + M;
write_item(X), crie uma aresta (Ti → Tj) no grafo de write_item(X);
precedência. read_item(Y);

3. Para cada caso S onde Tj executa um write_item(X) write_item(X);

após Ti executar um read_item(X), crie uma aresta Y := Y + N;


write_item(Y);
(Ti → Tj) no grafo de precedência.

4. Para cada caso S onde Tj executa um write_item(X) X


após Ti executar um write_item(X), crie uma aresta T2
(Ti → Tj) no grafo de precedência.
T1
5. O schedule é serializável se, e somente se, o grafo X
de precedência não tiver ciclos.
C não é serializável
Serialização de Schedules
Algoritmo: testando a serialização de conflito de S

1. Para cada transação Ti de S, crie um nó rotulado com Ti no grafo de precedência.

2. Para cada caso em S onde Tj executa um read_item(X) depois de Ti executar um write_item(X), crie uma aresta (Ti → Tj) no grafo de precedência.

3. Para cada caso S onde Tj executa um write_item(X) após Ti executar um read_item(X), crie uma aresta (Ti → Tj) no grafo de precedência.

4. Para cada caso S onde Tj executa um write_item(X) após Ti executar um write_item(X), crie uma aresta (Ti → Tj) no grafo de precedência.

5. O schedule é serializável se, e somente se, o grafo de precedência não tiver ciclos.

Exemplo: escalonamento f
X, Y
Transação T1 Transação T2 Transação T3
T1 T2
read_item(Y);
read_item(Z);
read_item(X);
write_item(X); Z, Y
write_item(Y); Y
write_item(Z);
read_item(Z); T3
read_item(Y);
write_item(Y);
read_item(Y); f é serializável
write_item(Y);
read_item(X);
write_item(X); Schedule serial equivalente: T3 →T1 →T2
Suporte para Transações em SQL, exemplo
EXEC SQL WHENEVER SQLERROR GOTO UNDO;

EXEC SQL SET TRANSACTION READ WRITE

DIAGNOSTIC SIZE 5 ISOLATION LEVEL SERIALIZABLE;

EXEC SQL INSERT INTO FUNCIONARIO (PNOME, UNOME, SSN, DNO, SAL)
VALUES ('Ana', 'Silva', '1233215', 2, 5000);

EXEC SQL UPDATE FUNCIONARIO SET SAL = SAL * 1.1 WHERE DNO = 2;

EXEC SQL COMMIT;

GOTO THE_END;

UNDO: EXEC SQL ROLLBACK;

THE_END: ...;

Consiste em primeiro inserir uma linha na tabela FUNCIONARIO e, depois, atualizar o salário de
todos os funcionários do departamento 2. Se houver algum erro em qualquer uma das instruções
SQL, a transação inteira é cancelada. Ou seja, nesse caso, qualquer salário seria restaurado ao valor
anterior e a linha inserida seria removida.
Técnicas de Bloqueio – Bloqueio Binário

❑ Um bloqueio binário possui dois estados:

✓ bloqueado (locked);

✓ desbloqueado (unlocked).

❑ As operações necessárias são:

• lock_item(X): bloqueia o item X;

• unlock_item(X): desbloqueia o item X.

❑ O bloqueio binário impõe a exclusão mútua no item de dado.

➢ Se uma operação de bloqueio ou desbloqueio de X for iniciada, nenhum

entrelaçamento é permitido até que a operação em questão termine ou a transação


espere.

➢ O comando de espera coloca a transação em uma fila de espera pelo item X até que

o mesmo seja desbloqueado.


Técnicas de Bloqueio – Bloqueio Binário

Algoritmos de bloqueio e desbloqueio do item X:

lock_item(X):

B: se LOCK(X) = 0 então (* item desbloqueado *)

LOCK(X) ← 1 (* bloquear o item *)

senão início

esperar até (LOCK(X) = 0 e o gerenciador de bloqueio despertar a transação);

goto B;

fim;

unlock_item(X):

LOCK(X) ← 0; (* desbloquear o item *)

se alguma transação estiver esperando então

despertar uma das transações em espera;


Técnica de Bloqueio Múltiplo não garante Serialização

Observe as seguintes aplicações T1 e T2, e o escalonamento (schedule) S:

Transação T1 Transação T2 Transação T1 Transação T2


read_lock(Y); read_lock(X); read_lock(Y);
read_item(Y); read_item(X); read_item(Y);
unlock(Y); unlock(X); unlock(Y);
write_lock(X); write_lock(Y); read_lock(X);
read_item(X); read_item(Y); read_item(X);
X := X + Y; Y := X + Y; unlock(X);
write_item(X); write_item(Y); write_lock(Y);
unlock(X); unlock(Y); read_item(Y);
Y := X + Y;
write_item(Y);
unlock(Y);
Supondo os valores iniciais, X = 20 e Y = 30. write_lock(X);
a) O escalonamento T1 seguido de T2 read_item(X);
X := X + Y;
resultaria em quais valores de X e Y? write_item(X);
unlock(X);
b) O escalonamento T2 seguido de
T1resultaria em quais valores de X e Y? Respostas:

c) E no escalonamento S, quais os valores a) X = 50 e Y = 80 (T1 seguida de T2)

resultantes de X e Y? b) X = 70 e Y = 50 (T2 seguida de T1)


c) X = 50 e Y = 50 (em S)
Técnica de Bloqueio Múltiplo não garante Serialização

Observe as seguintes aplicações T1 e T2, e o escalonamento (schedule) S:

Transação T1 Transação T2 Transação T1 Transação T2


read_lock(Y); read_lock(X);
read_lock(Y);
read_item(Y); read_item(X);
unlock(Y); unlock(X); read_item(Y);
write_lock(X); write_lock(Y); unlock(Y);
read_item(X); read_item(Y);
read_lock(X);
X := X + Y; Y := X + Y;
write_item(X); write_item(Y); read_item(X);
unlock(X); unlock(Y); unlock(X);
write_lock(Y);
read_item(Y);
Y := X + Y;
▪ S, portanto, não é serializável.
write_item(Y);
unlock(Y);
▪ Os itens Y em T1 e X em T2 foram write_lock(X);
desbloqueados cedo demais. read_item(X);
X := X + Y;
write_item(X);
unlock(X);
Deadlocks – Prevenção – com Registros de Timestamp

Wait-die (esperar-morrer)
❑ Se TS(Ti) < TS(Tj), ou seja, Ti mais antiga do que Tj, então Ti é
autorizada a esperar e Tj é abortada (morre) e reiniciada
depois, com o mesmo valor de registro de timestamp.

Wound-wait (ferir-esperar)
❑ Se TS(Ti) < TS(Tj), ou seja, Ti mais antiga do que Ti, aborta Tj (Ti
fere Tj) e a reinicia mais tarde, com o mesmo rótulo de tempo.
Deadlocks – Prevenção – com Registros de Timestamp

Resumindo:
❑ Em ambas as técnicas, a transação mais nova é abortada.
❑ Em esperar-morrer, a transação mais antiga espera, enquanto
a mais nova é abortada e reiniciada com o mesmo rótulo de
tempo.
❑ Em ferir-esperar a transação mais antiga apodera-se da mais
nova ao abortá-la.
As duas técnicas podem fazer com que algumas transações sejam
abortadas e reiniciadas sem necessidade – nem sempre elas
causariam um deadlock.
Detecção de Deadlocks – Grafo de Espera

❑ Uma forma de se detectar um estado de deadlock é construir e manter um grafo de

espera.

▪ Um nó é criado no grafo de espera para cada transação que esteja sendo

executada no momento.

▪ Uma aresta direcionada (nó Ti → nó Tj) é criada no grafo de espera sempre que

uma transação Ti estiver esperando para bloquear um item que esteja

bloqueado por uma transação Tj.

▪ Quando Tj libera o bloqueio nos itens que Ti está esperando, a aresta

direcionada é retirada do grafo de espera.

▪ Há um deadlock se, e somente se, o grafo de espera tiver um ciclo.


Detecção de Deadlocks - Timeout

❑Outra forma de se detectar um estado de deadlock é verificar o

timeout (intervalo, limite de tempo) definido pelo sistema.

▪ Se uma transação espera por um período maior do que o

período de timeout definido, o sistema supõe que a

transação pode estar em deadlock e aborta a mesma,

independentemente do fato do deadlock existir ou não.

❑Consiste em um método prático devido ao seu baixo nível de

overhead e à sua simplicidade.


Starvation

❑ Starvation ocorre quando uma transação não pode continuar sua execução em um intervalo

indefinido de tempo, enquanto outras transações são executadas normalmente.

❑ Na prática, starvation pode ocorrer quando:

1. O esquema de espera para itens bloqueados for injusto, priorizando algumas transações em

relação a outras;

▪ Solução 1: quanto mais uma transação espera, mais se deve aumentar a sua prioridade de

execução.

▪ Solução 2: utilizar um esquema de fila de tal forma que a primeira transação a chegar será a

primeira a ser atendida.

2. A mesma transação, no processo de seleção de vítima, for escolhida como vítima repetidamente.

▪ Solução: aumentar a prioridade de execução de transações que tenham sido abortadas

inúmeras vezes.

Você também pode gostar