Nível de isolamento de uma transacção |
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. |
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.
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. |
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.
Realizado por Turtle Learning ®. Última alteração em 2011-02-26