Você está na página 1de 5

ESCOLA S ECUNDÁRIA JOÃO G ONÇALVES ZARCO -402011

CURSO PROFISSIONAL DE TÉCNICO DE INFORMÁTICA - S ISTEMAS


SBDAW – 11º 10
UFCD0797– Administração de Bases de Dados
FICHA DE EXERCÍCIOS (BLOQUEIOS EM TRANSAÇÕES)–LOCK, DEAD-LOCK, LOCK IN SHARE MODE

1 – Bloqueios (Locks)
Numa base de dados como o MySQL os dados são consultados e alterados concorrentemente
pelos vários utilizadores. O que acontece se dois utilizadores tentarem alterar o mesmo dado
ao mesmo tempo? O primeiro a chegar ativa um bloqueio (LOCK) que só é retirado quando a
sua transação terminar. A segunda transação terá que esperar que o bloqueio seja retirado, o
que só acontece quando a primeira transação termina.

1.1 - Bloqueios com atualização de dados


O exemplo abaixo mostra como uma atualização de dados ativa um bloqueio.

TEMPO UTILIZADOR 1 UTILIZADOR 2 COMENTÁRIOS


1 commit; Garantir que o utilizador 1 não tem transações
pendentes.
2 commit;
Garantir que o utilizador 1 não tem
transações pendentes.
3 drop table if
exists teste; elemina uma tabela se houver uma com o
nome de teste
create table
teste (
id integer,
cria a tabela teste com os atributos id como
nome varchar(50),
chave primaria e nome
primary key
(id)
)
engine=innodb
charset=latin1

collate=latin1_bi
n ;
4 starttransaction;
comeca uma transacao
5 insertinto teste (id,nome)
inserir nos atributos id e nome os valores
values
(1,'José')
,(2,'João')
,(3,'joão')
,(4,'joaquim')
,(5,'josé')
,(6,'Joaquim');
rever todos atrivutos inseridos
select * from teste;
6 commit;
confirmar a transcao
7 starttransaction;
comeca uma transacao
8 update teste set
atualizar na tabela teste para nome “11111”
nome=’111111’ where
onde o id = 5
id=5;
Essa linha recebe um bloqueio (LOCK).
9 starttransactio
n; comeca uma transacao no utilizador 2

10 update teste set


TEMPO UTILIZADOR 1 UTILIZADOR 2 COMENTÁRIOS
nome=’JJJJJJJ’ where
id=4; O select mostra que a alteração ainda não está
select * from teste; confirmada.
11 update teste set O utilizador 2 vai tentar alterar a mesma linha
nome=’JOSE’ where que o utilizador 1, mas como tem um bloqueio
id=5; (LOCK) vai ficar à espera. A base de dados
espera algum tempo, findo o qual dá um erro:
“Lockwaittimeoutexceeded”
12 ------ Esperar que apareça o erro
“Lockwaittimeoutexceeded” na sessão do
utilizador 2. O segundo UPDATE não foi
executado.
13 select * from teste; A alteração feita pelo utilizador 1 teve sucesso,
mas não está confirmada. O bloqueio (LOCK)
ainda está ativo.
14 commit; A alteração feita pelo utilizador 1 foi confirmada.
O bloqueio (LOCK) é retirado. O comando do
utilizador 2 continuaria se não tivesse excedido o
tempo de espera.
15 rollback; Perante o erro obtido o utilizador 2 pode executar
uma das seguintes alternativas: repetir o comando
e continuar com a transação, ROLLBACK de
toda a transação r terminar a transação. Optando
por ROLLBACK.
16 select * from teste; A alteração do utilizador 1 foi confirmada. Das
alterações do utilizador 2 nenhuma teve sucesso.

1.2 - Bloqueios infinito (deadlock)

A sequência de comandos abaixo cria duas transações que se vão bloquear mutuamente,
provocando um “DEAD-LOCK”:

TEMPO UTILIZADOR 1 UTILIZADOR 2 COMENTÁRIOS


1 commit; Garantir que o utilizador 1 não tem transações
pendentes.
2 commit; Garantir que o utilizador 2 não tem transações
pendentes..
3 drop table if
exists teste; elemina uma tabela se houver uma com o
nome de teste
create table
teste ( cria a tabela teste com os atributos id como chave
id integer, primaria e nome
nome varchar(50),
primary key
(id)
)
engine=innodb
charset=latin1
collate=latin1_bi
n ;
4 starttransaction;
comeca uma transacao
5 insertinto teste (id,nome)
inserir nos atributos id e nome os valores
values
(1,'José')
,(2,'João')
,(3,'joão')
,(4,'joaquim')
TEMPO UTILIZADOR 1 UTILIZADOR 2 COMENTÁRIOS
,(5,'josé')
,(6,'Joaquim'); rever todos valores atribuidos
select * from teste;
6 commit;
confirmar a transcao
7 starttransaction;
comeca uma transacao no utilizador 1
8 starttransaction;
comeca uma transacao no utilizador 2
9 update teste set O utilizador 1 vai alterar o nome na linha com
nome=’AAAA’ where id=1, para AAA. Essa linha recebe um bloqueio
id=1; (LOCK).
10 update teste set O utilizador 2 vai alterar o nome na linha com
nome=’CCCC’ where id=5. Essa linha recebe um bloqueio (LOCK).
id=5;
11 update teste set O utilizador 1 vai tentar alterar a mesma linha
nome=’BBBB’ where que o utilizador 2 está a bloquear. A sua
id=5; transação vai ficar à espera.
12 update teste set O utilizador 2 vai alterar a mesma linha que o
nome=’DDDD’ where utilizador 1 está a bloquear. A sua transação vai
id=1; ficar à espera.
13 As duas transações estão à espera uma da
outra. A base de dados deteta o erro e
interrompe a execução da segunda
provocando-lhe ROLLBACK.

Um DEAD-LOCK é uma situação em que a segunda transação está à espera de um recurso que
está bloqueado pela primeira, enquanto a primeira espera por um recurso que está bloqueado
pela segunda. Teríamos uma espera infinita se não fosse o mecanismo existente na base de
dados que deteta automaticamente as situações de DEAD LOCK, provocando o ROLLBACK
automático da segunda transação.
Se na sequência de comandos anterior o utilizador demorar muito tempo a introduzir os
comandos 11 e 12 poderá não obter um DEAD LOCK. Isto porque, se for excedido o tempo de
espera pela libertação de um bloqueio os comandos são abortados, como foi visto no ponto
anterior.
Para evitar situações de DEAD LOCK recomenda-se o seguinte:
 As transações devem ser tão curtas quanto possível;
 Todas as transações devem alterar os dados na mesma ordem. Por exemplo
primeiro o id=1 e depois o id=5;
1.3 – Lock in share mode e a integridade referencial
Vamos criar um cenário em que uma tabela PARENT tem um relacionamento com uma tabela
CHILD, possuindo a base de dados uma restrição de integridade referencial. O utilizador 2 vai
executar uma transação onde começa por verificar que existe a linha com ID=2 em PARENT e
vai tentar inserir em CHILD uma linha que se relaciona com esse PARENT. Depois da verificação
e antes do INSERT o utilizador 1 vai eliminar a linha de PARENT e por isso o INSERT do
utilizador 2 vai falhar.
TEMPO UTILIZADOR 1 UTILIZADOR 2 COMENTÁRIOS
1 starttransaction; Iniciar uma nova transação.
2 drop table if exists
child;
drop table if exists
parent;
TEMPO UTILIZADOR 1 UTILIZADOR 2 COMENTÁRIOS
create table parent Criar tabela PARENT e CHILD e
( inserir registos.
id integer,
Os comandos DDL fazem COMMIT
nome varchar(50),
implícito, mas não terminam a
primary key (id)
transação.
);
create table child
(
id integer,
nome varchar(50),
id_parent integer,
primary key (id),
foreign key
(id_parent)
references
parent(id)
);
insert into parent
(id,nome) values
(1,'aaaa')
,(2,'bbbb');
insert into child
(id,nome,id_parent)
values
(1,'child aaaa',1)
,(2,'child
bbbb',1);
3 commit;
confirmar a transcao
4 starttransaction;
comeca uma transacao no utilizador
2
5 select * from Validar na tabela PARENT existe uma
parent where id=2 linha com ID 2, já que vamos tentar
inserir dados em CHILD associados a
este PARENT.
6 start transaction;
comeca uma transacao no utilizador
delete from parent where 2
id=2;

apagar from oarent onde o id e 2


commit;
confirmar a transcao
7 insert into child O registo da tabela PARENT foi
(id,nome,id_paren removido e por isso o insert não tem
t) values sucesso porque o registo “filho” não
(3,'child tem o respetivo “pai”
cccc',2);
8 commit;
confirmar a transcao

Para resolver este problema o SELECT do utilizador 2 deveria ter sido feito com um bloqueio:
 select * fromparentwhere id=2 lock in share mode – é atribuído um bloqueio do tipo
SHARED LOCK;
 select * fromparentwhere id=2 for update – é atribuído um bloqueio do tipo
EXCLUSIVE LOCK;
Depois de atribuído um dos dois bloqueios acima, o utilizador 1 não consegue fazer o DELETE
(nem qualquer outra alteração) enquanto a transação do utilizador 2 estiver a decorrer.

Você também pode gostar