SQL em Oracle > DML > Nível de isolamento de uma transacção
Nível de isolamento de uma transacção tl_logo2.jpg

Vamos considerar uma transacção que apenas lê dados. Enquanto está a ser executada, o que pode demorar uns segundos ou algumas horas, os dados que ela lê podem ser alterados por outras transacções, o que coloca em perigo a integridade do seu resultado. Este problema coloca-se apenas com os dados de leitura, pois quando a transacção altera dados a base de dados activa LOCKS que impedem a sua modificação em simultâneo por outras transacções.

Em termos teóricos, enquanto a transacção decorre os dados por ela vistos não podem ser alterados. A solução fácil para este problema é bloquear os dados que estão a ser lidos(LOCK), o que impediria alterações por outros utilizadores. Mas isto viola um princípio importante: numa base de dados quem lê dados não pode prejudicar quem escreve. Para ultrapassar este problema a base de dados deve fornecer um mecanismo que permita à transacção, durante a sua duração, trabalhar com a imagem inicial dos dados, tornando-a assim imune a alterações.

O nível de isolamento de uma transacção mede a independência desta em relação às alterações nos dados por ela lidos feitas por outras transacções. Uma transacção tem um elevado nível de isolamento se for absolutamente imune a essas alterações. Pelo contrário, será pouco isolada se os seus resultados perderem integridade com as alterações feitas por outras transacções.

A norma SQL92 define 4 níveis de isolamento de transacções aos quais correspondem diferentes graus de impacto nos resultados. Estes níveis são definidos tendo em conta 3 situações que podem ocorrer quando são executadas simultaneamente duas transacções que acedem aos mesmos dados concorrentemente:

O quadro abaixo descreve a forma como os 4 níveis de isolamento definidos pela norma SQL92 se comportam perante as 3 situações referidas antes:

Nível de isolamento DIRTY READS NONREPEATABLE (FUZZY) READS PHANTOM READS
READ UNCOMMITED Pode ocorrer Pode ocorrer Pode ocorrer
READ COMMITED Não ocorre Pode ocorrer Pode ocorrer
REPEATABLE READ Não ocorre Não ocorre Pode ocorrer
SERIALIZABLE Não ocorre Não ocorre Não ocorre

Os níveis de isolamento disponibilizados na base de dados Oracle são READ COMMITTED e SERIALIZABLE. Por omissão funciona no modo READ COMMITTED o que significa que não é possível ocorrerem DIRTY READS, mas podem ocorrer NONREPEATABLE (FUZZY) READS ou PHANTOM READS. O exemplo a seguir apresentado comparam os dois níveis de isolamento e a forma como a transacção é por eles afectada.


READ COMMITTED

O nível de isolamento usado por omissão pela base de dados Oracle é READ COMMITED. No exemplo abaixo vamos abrir duas sessões e vamos executar comandos em cada uma delas.

Tempo Sessão 1 Sessão 2 Comentário
1
drop table teste;
create table teste (
   id number(10,0),
   nome varchar2(10),
   constraint pk_teste primary key (id)
);
Na Sessão 1 removemos a tabela TESTE (caso exista) e recriamo-la. São dois comandos DDL, sendo cada um deles uma transacção com COMMIT implícito. Depois do COMMIT implícito do segundo comando inicia-se uma nova transacção na Sessão 1.
2
commit;
select * from teste;
Forçar o inicio de uma nova transacção na Sessão 2 e verificar que a nova tabela já é visível.
3
insert into teste (id,nome) 
    values (1,'aaaaaa');
insert into teste (id,nome) 
    values (2,'bbbbbb');
insert into teste (id,nome) 
    values (3,'cccccc');
A Sessão 1 insere várias linhas na tabela TESTE mas não confirma as alterações.
4
select * from teste;
As alterações feitas na tabela TESTE pela Sessão 1 são visiveis na própria sessão.
5
select * from teste;
As alterações feitas na tabela TESTE pela Sessão 1 ainda não são visíveis na Sessão 2 pois ainda não estão confirmadas.
6
commit;
A Sessão 1 confirma as alterações e termina a transacção actual.
7
select * from teste;
As alterações feitas na tabela TESTE pela Sessão 1 ficam visíveis na Sessão 2 visto que já foram confirmadas (READ COMMITED).
É a terceira vez que executamos o mesmo SELECT dentro da mesma transacção e obtemos um resultado diferente. Estamos numa situação PHANTOM READ.
8
update teste
  set nome = 'CCCCCC'
  where id=3;
commit;
select * from teste;
A Sessão 1 executa um UPDATE e confirma a alteração, terminando a transacção. Esta alteração é visível na Sessão 1.
9
select * from teste;
A Sessão 2 continua na mesma transacção e executa o mesmo SELECT pela quarta vez, obtendo um resultado diferente das execuções anteriores. Estamos numa situação NONREPEATABLE (FUZZY) READ.
10
delete from teste 
  where id = 2;
commit;
select * from teste;
A Sessão 1 apaga uma linha e confirma a alteração, terminando a transacção. A alteração é visivel na Sessão 1.
11
select * from teste;
A Sessão 2 continua na mesma transacção e executa o mesmo SELECT pela quinta vez, obtendo um resultado diferente das execuções anteriores. Estamos numa situação NONREPEATABLE (FUZZY) READ.
12
insert into teste (id,nome) values (4,'dddddd');
commit;
select * from teste;
A Sessão 1 insere uma linha e confirma a alteração, terminando a transacção. A alteração é visivel na Sessão 1.
13
select * from teste;
A Sessão 2 continua na mesma transacção e executa o mesmo SELECT pela sexta vez, obtendo um resultado diferente das execuções anteriores. Estamos numa situação PHANTOM READ.
14
commit;
A Sessão 2 termina a transacção actual.
15
select * from teste;
A Sessão 2 continua a ver o que já via.

note04.gif

Este nível de isolamento evita DIRTY READS, mas não garante REPEATABLE READS. Por ser um bom compromisso entre desempenho e coerência de dados é o nível de isolamento usado por omissão na base de dados Oracle.

bthome.gifTopo


SERIALIZABLE

Se o utilizador quizer um elevado nível de isolamento para a sua transacção a base de dados disponibiliza o modo de funcionamento SERIALIZABLE, que pode ser activado ao nível da sessão ou ao nível da transacção, usando os comandos abaixo:

--alterar ao nível da sessão
ALTER SESSION SET ISOLATION_LEVEL = SERIALIZABLE;

--alterar ao nível da transacção
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

O nível de isolamento por omissão pode ser reposto com os comandos abaixo:

--alterar ao nível da sessão
ALTER SESSION SET ISOLATION_LEVEL = READ COMMITTED;

--alterar ao nível da transacção
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

No exemplo abaixo os comandos executados nas duas sessões são identicos aos que foram usados no ponto anterior, mas a sessão 2 activa o modo SERIALIZABLE.

Tempo Sessão 1 Sessão 2 Comentário
1
drop table teste;
create table teste (
   id number(10,0),
   nome varchar2(10),
   constraint pk_teste primary key (id)
);
Na Sessão 1 removemos a tabela TESTE (caso exista) e recriamo-la. São dois comandos DDL, sendo cada um deles uma transacção com COMMIT implícito. Depois do COMMIT implícito do segundo comando inicia-se uma nova transacção na Sessão 1.
2
commit;
SET TRANSACTION 
    ISOLATION LEVEL SERIALIZABLE;
select * from teste;
Forçar o inicio de uma nova transacção na Sessão 2.
Definir ao nível da transacção o nível de isolamento SERIALIZABLE.
Verificar que a nova tabela já é visível.
3
insert into teste (id,nome) 
    values (1,'aaaaaa');
insert into teste (id,nome) 
    values (2,'bbbbbb');
insert into teste (id,nome) 
    values (3,'cccccc');
A Sessão 1 insere várias linhas na tabela TESTE mas não confirma as alterações.
4
select * from teste;
As alterações feitas na tabela TESTE pela Sessão 1 são visiveis na própria sessão.
5
select * from teste;
As alterações feitas na tabela TESTE pela Sessão 1 ainda não são visíveis na Sessão 2 pois ainda não estão confirmadas.
6
commit;
A Sessão 1 confirma as alterações e termina a transacção actual.
7
select * from teste;
As alterações feitas na tabela TESTE não ficam visíveis na Sessão 2, mesmo que já confirmadas, visto que foi definido como nível de isolamento SERIALIZABLE.
8
update teste
  set nome = 'CCCCCC'
  where id=3;
commit;
select * from teste;
A Sessão 1 executa um UPDATE e confirma a alteração, terminando a transacção. Esta alteração é visível na Sessão 1.
9
select * from teste;
A Sessão 2 continua na mesma transacção e executa o mesmo SELECT pela quarta vez, obtendo o memso resultado das execuções anteriores.
10
delete from teste 
  where id = 2;
commit;
select * from teste;
A Sessão 1 apaga uma linha e confirma a alteração, terminando a transacção. A alteração é visivel na Sessão 1.
11
select * from teste;
A Sessão 2 continua na mesma transacção e executa o mesmo SELECT pela quinta vez, obtendo o mesmo resultado das execuções anteriores.
12
insert into teste (id,nome) values (4,'dddddd');
commit;
select * from teste;
A Sessão 1 insere uma linha e confirma a alteração, terminando a transacção. A alteração é visivel na Sessão 1.
13
select * from teste;
A Sessão 2 continua na mesma transacção e executa o mesmo SELECT pela sexta vez, obtendo o mesmo resultado que nas execuções anteriores.
14
commit;
A Sessão 2 termina a transacção actual.
15
select * from teste;
A Sessão 2 finalmente vê os dados confirmados pela Sessão 1.

note04.gif

Como o isolamento foi definido ao nível da transacção, quando esta termina a sessão volta a trabalhar com o nível READ COMMITED. Do ponto de vista teórico o nível SERIALIZABLE é muito interessante, mas na prática obriga a base de dados a um trabalho extra muito pesado, pois tem que guardar uma fotografia dos dados num determinado instante no tempo. Isto consome espaço adicional nos segmentos de rollback, que têm armazenamento finito, e obriga a uma carga acrescida de I/O e CPU. Na maior parte das situações o nível READ COMMITED é suficiente, sendo por isso o nível por omissão. O programador deve estar atento às situações especiais onde é requerido o nível SERIALIZABLE para o activar.

Até há pouco tempo atrás algumas bases de dados disponíveis no mercado não faziam sequer READ COMMITED.

bthome.gifTopo


Realizado por Turtle Learning ®. Última alteração em 2011-02-26