Pedro F. Carvalho Analista de Sistemas contato@pedrofcarvalho

Propaganda
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
GERENCIANDO DADOS E CONCORRÊNCIA
Pl/SQL
PL/SQL significa "Procedural Language extensions to SQL". O PL/SQL pode ser usado
em bancos de dados Oracle, no Oracle Server, em ferramentas clientes, isto é, Oracle
Forms. O PL/SQL é muito similar à linguagem SQL mas acrescenta construções de
programação
similares
a
outras
liguagens.
Procedure
Para criar uma procedure. Uma procedure é um grupo de comandos PL/SQL que
realizam uma tarefa.
Sintaxe
CREATE [OR REPLACE] PROCEDURE [schema.]procedure
[(argument[IN I OUT I IN OUT] datatype
[,argument [ IN I OUT I IN OUT] datatype]...)]
{IS I AS} pl/sql_subprogram_body
Parâmetros
OR
REPLACE
Recria a procedure se esta já existir.
schema
Dono da procedure.
procedure
Nome da procedure.
argument
Parâmetro da procedure.
IN
Indica que você deve especificar o valor deste
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
parâmetro na chamada da procedure.
OUT
Indica que a procedure ira retornar um valor
por este parâmetro.
IN OUT
Através deste parâmetro deve-se informar um
valor para procedure e este voltara modificado.
datatype
Tipo de dado do parâmetro
pl/sql_subprogram_body
Codificação da procedure
Exemplo que cria a procedure totaliza_nota:
CREATE PROCEDURE totaliza_nota (p_cod_cliente IN number)
AS BEGIN
UPDATE nota_fiscal
SET val_total = val_total + (SELECT sum(valor * qtd) FROM itens WHERE
itens.nr_nota = nota_fiscal.nr_nota)
dt_processamento = sysdate
WHERE dt_processamento is NULL and cod_cliente = p_cod_cliente;
COMMIT;
END;
Alter Procedure
Para recompilar uma procedure.
Sintaxe
ALTER PROCEDURE [schema.]procedure COMPILE
Drop Procedure
Para remover uma procedure da base.
Sintaxe
DROP PROCEDURE [schema.]procedure
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
Executando Procedure
Exemplo
BEGIN
NomeProc(Param);
END;
Ou
EXEC NomeProc(Param);
Dentro de um bloco:
BEGIN
NomeProc(Param);
END;
/
FUNCTION
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
Create Function
Cria uma função de usuário.
Uma função é um conjunto de comandos PL/SQL normalmente utilizados para efetuar
um cálculo ou manipular um valor. Podem ser utilizadas dentro de comandos SQL.
Nas functions não é aconselhavel fazer alterações no status da base, por exemplo,
incluir, alterar, deletar registros. Ao alterar o status do banco, esta função não poderá
ser utilizada dentro de comandos SQL. Assim a utilidade da function é
descaracterizada, assumindo a função da procedure que é própria para alterar dados.
Sintaxe
CREATE [OR REPLACE] FUNCTION [schema.]function
[ (argument [IN] datatype
[, argument [IN] datatype] ...)]
RETURN datatype
{IS | AS} pl/sql_subprogram_body
Parâmetros
OR REPLACE
Recria a função se esta já existir.
schema
Dono da função.
function
Nome da função
argument
Nome de um parâmetro da função.
IN
Indica que você pode fornecer um valor no
momento da chamada da função.
datatype
É o tipo de dado do parâmetro
RETURN
datatype
Especifica o tipo de dado que a função deve
retornar.
pl/sql_subprogram_body
É o código da função.
Exemplo I
Função que calcula a media dos salário de um determinado departamento:
CREATE FUNCTION cal_media(p_deptno IN NUMBER)
RETURN NUMBER IS v_media number;
BEGIN
SELECT avg(sal)
INTO v_media
FROM emp
WHERE dept_no = p_deptno;
RETURN(v_media);
END;
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
A utilização da função criada acima em um comando SQL:
SELECT cal_media(deptno) FROM dept;
exemplo II
CREATE OR REPLACE FUCTION media(p_dept number DEFAULT -1)
RETURN NUMBER IS v_media number;
BEGIN
IF p_dept = -1 then
SELECT avg(salario) INTO v_media
FROM funcionario;
ELSE
SELECT avg(salario) INTO v_media
FROM funcionario WHERE cod_dep = p_dept;
END IF;
RETURN (v_media);
END;
Drop Function
Remove uma função da base.
Exemplo
DROP FUNCTION cal_media;
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
TRIGGERS
Create Trigger
Cria e habilita triggers. Trigger é um bloco de código PL/SQL associado a uma tabela.
Se um comando SQL for executado sobre esta tabela as triggers são disparadas
automaticamente pelo Oracle. Normalmente as regras de negócio são implementadas
em triggers.
Sintaxe
CREATE [OR REPLACE] TRIGGER [schema.]trigger
{BEFORE | AFTER}
{DELETE | INSERT | UPDATE [OF column [, column] ...]}
[OR {DELETE | INSERT | UPDATE [OF column [, column] ...]}] ...
ON [schema.]table
[ [REFERENCING { OLD [AS] old [NEW [AS] new]
| NEW [AS] new [OLD [AS] old] } ]
FOR EACH ROW
[WHEN (condition)] ]
pl/sql_block
Parâmetros
OR
REPLACE
schema
Recria a trigger se esta já existir.
Nome do usuário dono da trigger
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
table
Nome da tabela à qual a trigger esta associada
trigger
Nome da trigger que esta sendo criada.
BEFORE
Indica ao ORACLE para disparar a trigger antes
de executar o comando
AFTER
Indica ao ORACLE para disparar a trigger
depois de executar o comando
DELETE
Indica que esta trigger deve ser disparada
quando um comando DELETE apagar uma linha
da tabela.
INSERT
Indica que esta trigger deve ser disparada
quando um comando INSERT adicionar uma
linha da tabela.
UPDATE OF
Indica que esta trigger deve ser disparada
quando um comando UPDATE alterar uma das
colunas especificadas na clausula OF
REFERENCIN
G
Usado para nomes correlacionados. Você pode
usar correlação de nomes em blocos PL/SQL e
em cláusulas WHEN de uma trigger para fazer
referência de antigos e novos valores da linha
corrente.
FOR
ROW
EACH Indica que a trigger deve ser disparada a cada
linha afetada pelo comando
WHEN
Contém uma condição SQL que deve ser
verdadeira para permitir o disparo da trigger.
Bloco PL/SQL que indica a ação a ser
executada pela TRIGGER
Quando uma trigger é criada para mais que uma operação DML, pode-se utilizar
constantes condicionais no corpo da trigger para executar determinado bloco de
comandos, dependendo do comando que a disparou. As constantes condicionais são as
seguintes:
pl/sql_block
INSERTING
TRUE se o comando que disparou a trigger foi
um INSERT.
DELETING
TRUE se o comando que disparou a trigger foi
um DELETE.
UPDATING
TRUE se o comando que disparou a trigger foi
um UPDATE.
UPDATING (column_name)
TRUE se o comando que disparou a trigger foi
um UPDATE e a coluna column_name foi
alterada.
Obtendo o conteúdo dos campos em triggers:
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
:NEW.Nome_do_C
ampo
BEFORE
Se a trigger for
disparada
BEFORE
os
dados
podem
ser alterados.
Retorna o conteúdo
novo do campo.
No DELETE não é
utilizado pois não há
alteração de valores,
consequentemente
não há valor novo.
:OLD.Nome_do_C
ampo
AFTER
Se a trigger for
disparada
AFTER os dados
não podem ser
alterados.
BEFORE
Retorna o conteúdo
antigo do campo e
nunca
pode
ser
alterado.
AFTER
No INSERT não é
utilizado pois não há
valores antigos a
inserção.
Se a operação for
INSERT retornará o
valor que poderá ser
inserido
pelo
comando SQL. No
UPDATE
o
comportamento será
o
mesmo,
pois
retornará o valor do
campo
com
as
alterações
que
poderão ser feitas.
Se a operação for
INSERT retornará o
valor
que
foi
inserido
pelo
comando SQL. No
UPDATE
o
comportamento será
o
mesmo,
pois
retornará o valor do
campo
com
as
alterações que foram
feitas.
Se a operação for
UPDATE retornará o
valor do campo antes
da alteração. No
DELETE retornará o
conteúdo do campo
atual.
Se a operação for
UPDATE retornará o
valor do campo antes
da alteração. No
DELETE retornará o
conteúdo do campo
atual.
Quando existem diversas triggers a serem executadas (before ou after), não se pode
dizer qual será a ordem de execução dessas triggers.
Triggers
BEFORE
Constrain Gravação Triggers
ts
AFTER
Recomenda-se o uso das triggers before para correção dos dados gravados em :new,
pois é esse o momento ideal para isso. Consistência de valores, checagem de dados
após a gravação e operações similares deve-se usar triggers after.
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
Declarando variáveis dentro de uma trigger
A variável V1 é do tipo numérica.
A variável V2 é do tipo coluna de uma tabela, no exemplo, da tabela ITEM campo
VAL_UNITARIO.
A variável V3 é do tipo linha de uma tabela, no exemplo, da tabela ITEM.
CREATE OR REPLACE trg_nome1
BEFORE INSERT OR DELETE OR UPDATE ON ITEM
FOR EACH ROW
DECLARE
V1 NUMBER;
V2 ITEM.VAL_UNITARIO%TYPE;
V3 ITEM%ROWTYPE;
BEGIN
...
END;
Gerando uma mensagem de erro
Usando trigger para restringir operações em uma tabela. No exemplo abaixo, irá exibir
uma mensagem de erro quando a operação é diferente de uma inserção.
CREATE OR REPLACE TRIGGER trg_nome2
BEFORE INSERT OR DELETE OR UPDATE ON ITEM
FOR EACH ROW
BEGIN
IF NOT INSERTING THEN
RAISE_APPLICATION_ERROR (-20000, 'Erro: não é permitida está
operação!');
END IF;
END;
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
Exemplo I
O exemplo seguinte usa as constantes condicionais para determinar qual tipo de
comando SQL foi feito na tabela classified_table:
CREATE TRIGGER audit_trigger
BEFORE INSERT OR DELETE OR UPDATE ON classified_table
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO audit_table
VALUES (USER || ' inseriu ' || ' novo valor: ' || :new.campo);
ELSIF DELETING THEN
INSERT INTO audit_table
VALUES (USER || ' apagou ' || ' valor antigo: ' || :old.campo);
ELSIF UPDATING('FORMULA') THEN
INSERT INTO audit_table
VALUES (USER || ' alterou ' || ' formula antiga: ' || :old.formula || '
formula nova: ' || :new.formula);
ELSIF UPDATING THEN
INSERT INTO audit_table
VALUES (USER || ' alterou ' || ' valor antigo: ' || :old.campo || ' valor
novo: ' || :new.campo);
END IF;
END;
Exemplo II
Este exemplo cria uma trigger que só permite alterações na tabela EMP sejam feitas
somente no horário comercial:
CREATE TRIGGER scott.emp_permit_changes
BEFORE DELETE OR INSERT OR UPDATE ON scott.emp
DECLARE
dummy INTEGER;
BEGIN
/* Se for Sábado ou Domingo retorna ERROR.*/
IF (TO_CHAR(SYSDATE, 'DY') = 'SAT' OR TO_CHAR(SYSDATE, 'DY') = 'SUN')
THEN
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
raise_application_error( -20501, 'Você não pode alterar funcionário no
fim de semana');
END IF;
/* Compara hoje com todos os feriados da empresa. Se é feriado hoje,
então retorna ERRO.*/
SELECT COUNT(*) INTO dummy FROM company_holidays
WHERE day = TRUNC(SYSDATE);
IF dummy > 0 THEN
raise_application_error( -20501, 'Não é permitido alterar funcionário no
feriado');
END IF;
/*Se o horário atual for menor que 8:00AM ou maior 6:00PM, então retorna
ERRO. */
IF (TO_CHAR(SYSDATE, 'HH24') < 8 OR TO_CHAR(SYSDATE, 'HH24') >= 18)
THEN raise_application_error( -20502, 'Só pode alterar funcionário no horário
do expediente');
END IF;
END;
Exemplo III
Este exemplo cria uma trigger que quando um novo funcionário é adicionado ou o
salário de um já existente é alterado, garante que o valor do salário esteja na faixa da
função do funcionário:
CREATE TRIGGER scott.salary_check
BEFORE INSERT OR UPDATE OF sal, job ON scott.emp
FOR EACH ROW
WHEN (new.job <> 'PRESIDENT')
DECLARE
minsal NUMBER;
maxsal NUMBER;
BEGIN
/* Pega o menor e o maior salário da função. */
SELECT minsal, maxsal INTO minsal, maxsal FROM sal_guide
WHERE job = :new.job;
/* Se o salário é menor que o mínimo ou maior que o máximo gera um ERRO */
IF (:new.sal < minsal OR :new.sal > maxsal) THEN
raise_application_error( -20601, 'Salario ' || :new.sal || ' fora da faixa para '
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
|| :new.job || ' do funcionario ' || :new.ename );
END IF;
END;
PACKAGES
O que é uma PACKAGE? Resposta, são áreas de armazenamentos de sub-programas,
constantes, váriaveis e cursores em PL/SQL que dependendo do modo que você
construir, você poderá compartilhar as informações desta PACKAGE com outros
aplicativos.
Uma PACKAGE é constituida de duas partes. A primeira é a PACKAGE SPECIFICATION
e a seguda é a PACKAGE BODY, onde atráves delas podemos definir a função do nosso
"pacote" dentro do banco de dados. A seguir irei comentar um pouco sobre cada parte.
PACKAGE SPECIFICATION
Tem como função de criar a interface de suas aplicações e definir os tipos de váriaveis,
cursores, exceções, nomear rotinas, e funções, tudo que se define na parte de
SPECIFICATION do seu PACKAGE poderá ser compartilhado com outros scripts ou
programas em SQL e PL/SQL.
PACKAGE BODY
Está parte da sua PACKGE tem como função executar por completo todas as suas
rotinas, cursores e etc, que você definiu na SPECIFICATION, deste modo, todas as
funções executadas dentro do BODY não poderão ser compartihadas com outras
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
aplicações, além disso no BODY você poderá criar detalhes e declarações que ficarão
invisíveis para outras aplicações.
Abaixo está um exemplo em SQL de PACKAGE SPECIFICATION e BODY.
Legenda dos códigos.
PACKAGE SPECIFICATION
PACKAGE BODY
SQL
CREATE OR REPLACE PACKAGE nome_package IS
>
2
TYPE REGTESTE IS RECORD (NOME VARCHAR2(50), SEXO CHAR(1));
3
CURSOR CTESTE (MATRICULA IN NUMBER) RETURN REGTESTE
4
FUNCTION EXCLUIR (MATRICULA IN NUMBER) RETURN BOOLEAN;
5
END nome_package;
6
/
SQL
CREATE OR REPLACE PACKAGE BODY nome_package IS
>
2
CURSOR CTESTE (MATRICULA IN NUMBER) RETURN REGTESTE IN
3
SELECT NOME, SEXO FROM Tabela WHERE SEXO=M;
4
BEGIN
5
DELETE FROM Tabela WHERE SEXO=M;
6
END;
7
END nome_package;
8
/
Como dito, percebemos que a PACKAGE SPECIFICATION foi compartilhada com a
PACKAGE BODY e a própria BODY não poderá ser compartilhada com outras aplicação.
Assim permitimos que alguma mudança na programação em SPEFIFICATION poderá
afetar outras aplicações que estão utilizando o PACKAGE.
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
LOCK – CONFLITO DE BLOQUEIO NO ORACLE
O Oracle como outros bancos de dados, deve gerenciar múltiplos usuários acessando a
mesma data simultaneamente, garantindo o controle de concorrência do banco de
dados.
Conflitos de bloqueios (lock conflicts) ocorrem quase que frequentemente durante as
operações das transações, mais geralmente são quase todas resolvidos pelo próprio
mecanismo de fila da Oracle. Para entender melhor o lock conflicts observe a tabela
abaixo.
TRANSAÇÃO 1
TEMPO
TRANSAÇÃO 2
UPDATE
PRODUTO
SET
valor
=
30
WHERE id_produto = 1;
08:00
UPDATE
SET
valor
WHERE id_produto = 2;
1 row updated.
UPDATE produto SET valor = 52
WHERE id_produto = 2;
=
PRODUTO
50
1 row updated.
08:15
Transação continua normal, utilizando
selects, inserts, updates e até mesmo
deletes na mesma ou em outras
tabelas.
Sessão ainda aguardando
08:20
Transação continua normal, utilizando
selects, inserts, updates e até mesmo
deletes na mesma ou em outras
tabelas.
1 row updated.
08:25
commit;
Nenhuma
linha
atualizada
no
momento, pois a transação 1 entra
em lock conflict com a transação 2.
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
Liberado o lock, a sessão continua
normalmente.
A tabela mostra a atividade, de duas transações. Observe no horário das 08:15 que a
transação 1 não conseguiu fazer o update, pois a transação 2 estava como lock na
linha. Somente as 08:25, quando o commit da transação 2 é feito, o lock é liberado
que a transação 1 consegui realizar a operação.
Como em nenhum ambiente o cenário é perfeito (como o do meu quadro
apresentado), o usuário não vai ficar esperando cinco minutos um simples update de
outra transação ser liberado para ele trabalhar, ele vai imediatamente contatar você
que o banco está lendo, não esta funcionando, e por ai vai…
Evitando os lock conflicts.
Obviamente para se ter um pouco de prevenção dos conflitos, é necessário assegurar
que nenhuma transação execute por longos períodos de tempo sem um commit (DDL,
DCL). A cada mudança, assim que necessário, pedir para o LGWR trabalhar (processo
de commit), fazendo a gravação dos dados persistente nos arquivos de Redo e
consequentemente nos Datafiles
Detectando os lock conflicts.
É realmente fácil detectar usuário que sofrem bloqueios e os que fazem o bloqueio.
Para isso criaremos um ambiente para o melhor entendimento do conceito. O script
abaixo cria o usuário usr1 e o usuário usr2.
SQL>
create
2
3
identified
4
quota
5 account unlock;
user
on
usr1
default
oracle
users
on
usr2
default
oracle
users
profile
by
unlimited
Usuário criado.
SQL>
create
2
3
identified
4
quota
5 account unlock;
user
profile
by
unlimited
Usuário criado.
SQL> grant create table, connect to usr1, usr2;
Concessão bem-sucedida.
Agora com o usuário usr1 criamos a tabela de produto e disparamos dois insert.
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
SQL>
2
3
4
5
6/
create
table
id_produto
number
produto
(
key,
number(10,2)
)
primary
valor
Tabela criada.
SQL>
insert
into
Informe
o
Informe o valor para valor: 50
produto
valor
values
para
valor
para
(&id,
id:
&valor);
1
id:
/
2
1 linha criada.
SQL>
Informe
o
Informe o valor para valor: 5
1 linha criada.
SQL> commit;
Commit concluído.
Ainda como o usuário usr1, vamos dar a permissão de update para o usuário usr2,
assim
garantimos
que
o
usr2
poderá
acessar
a
tabela
de
usr1.
SQL> grant update on produto to usr2;
Concessão bem-sucedida.
Agora sim, nosso ambiente está pronto para detectar os locks. Com o usuário usr1
faça um update atualizando a coluna valor para 35 onde o id_produto é igual a 1.
SQL>
update
2 where id_produto = 1;
produto
set
valor
=
35
1 linha atualizada.
Em outro terminal, com o usuário usr2 também atualizamos a coluna valor onde o
id_produto
é
igual
a
1,
observe
o
lock
entrando
em
ação.
SQL>
2
_
update
where
usr1.produto
set
id_produto
valor
=
=
36
1;
A sessão do usr2 fica aguardando algum commit da transação do usr1. Para conseguir
detectar sessões que estão bloqueando outras sessões, basta executar o select baixo.
SQL>
select
username
from
v$session
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
2
3
4
5/
where
select
sid
blocking_session
in
(
v$session
)
from
USERNAME
-----------------------------USR1
Verificando
usuários
que
estão
aguardando
SQL>
select
sid,
2
from
3 where blocking_session is not null;
SID
----136 USR2
commit
de
username,
USERNAME
------------
outra
sessão.
blocking_session
v$session
BLOCKING_SESSION
----------------
141
Usuários que estão com o lock, e usuário que estão aguardando a outra transação.
SQL>
select
sid,
username,
2
from
v$session
where
blocking_session
3
UNION
4
select
A.sid,
A.username,
5
from
v$session
A,
6 where A.sid = B.blocking_session;
SID
---136
141 USR1
USERNAME
----------
blocking_session
not
null
ALL
A.blocking_session
v$session
B
is
BLOCKING_SESSION
---------------141
USR2
Resolvendo lock conflicts.
Somente é possível resolver conflitos de bloqueios através de duas maneiras:
1)
Emitir
um
commit
na
2) Terminar a sessão que está bloqueando.
sessão
que
está
bloqueando
A melhor maneira de resolver é contatar o usuário que está segurando o lock e pedir
para liberar, afim de poder completar a transação. Como nem sempre está opção é
possível, existe uma maneira mais rápida radical, que seria finalizar a sessão pelo
modo kill do Oracle.
ALTER SYSTEM KILL SESSION ‘SID, SERIAL#’ IMMEDIATE;
SQL>
select
2 from v$session where username = 'USR1';
sid,
serial#
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
SID
SERIAL#
---------141
46
----------
SQL> alter system kill session '141, 46' immediate;
Sistema alterado.
Depois de alguns segundos, se voltarmos para o terminal do usuário usr2, pode
observar que o update antes que estava aguardando terminar o lock do usuário usr1, é
atualizada.
1 linha atualizada.
SQL>
Por fim, aqui está outras views com informações de lock disponível no Oracle:
V$ENQUEUE_LOCK
V$EVENT_NAME
V$LOCK
V$_LOCKV
$LOCKED_OBJECT
V$LOCKS_WITH_COLLISIONS
V$LOCK_ACTIVITY
V$LOCK_ELEMENT
MAIS VIEWS
GV$ENQUEUE_LOCK
GV$LOCK_ELEMENT
GV$LOCKS_WITH_COLLISIONS
GV$LOCKED_OBJECT
GV$LOCK
GV$EVENT_NAME
GV$LOCK_ACTIVITY
Lock de linha
Um lock do tipo linha é aplicado individualmente às linhas de uma tabela. Quando uma
linha está ““locada””, nenhuma outra sessão conseguirá alterá-la até que a transação
que detém o lock chegue ao fim. Claro que se uma determinada linha de uma tabela
estiver ““locada”” por uma determinada transação, outros processos poderão sem
problema algum alterar as outras linhas dessa tabela que ainda não estejam
““locada”s”.
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
Lock de tabela
Esse tipo de lock é aplicado no nivel tabela e pode ser utilizado para obter uma
imagem consistente da tabela e para assegurar que sua definição não mude enquanto
transações ativas ainda existirem nela.
Tipos de Locks
No Oracle, os seguintes tipos de locks são possíveis.
Tipo
Row Share (RS)
Row Excluive (RX)
Share (S)
Share Row Exclusive (SSX)
Share Row Exclusive (SRX)
Exclusive (X)
Level
Tabela
Tabela
Tabela
Tabela
Tabela
Tabela/Linha
Para entender de forma integral e para usufruir e resolver problemas utilizando a view
V$LOCK é importante entender cada um desses tipos de locks. Vejamos a seguir uma
explicação de cada um deles.
Row Share (RS)
Também é conhecido com subshare lock (SS), esse lock indica que a transação possui
linhas ““locada”s” exclusivamente, mas ainda não as alterou. É obtido através da
instrução da Listagem 1.
SELECT .....FROM table...FOR UPDATE...
LOCK TABLE table IN ROW SHARE MODE;
Listagem 1. Lock do tipo Row Share.
Um lock RS pode ser utilizado para garantir que a tabela não será “locada”
exclusivamente antes de sua transação alterá-la.
Row Exclusive (RX)
Também conhecido como subexclusive lock (SX), esse lock indica que a transação
possui linhas “locadas” exclusivamente e já as alterou. É obtido através das instruções
da Listagem 2.
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
INSERT INTO tabela...;
UPDATE tabela...;
DELETE FROM tabela...;
LOCK TABLE table IN ROW EXCLUSIVE MODE;
Listagem 2. Lock do tipo Row Exclusive.
Share (S)
Esse tipo de lock é obtido através da instrução da Listagem 3.
LOCK TABLE table IN SHARE MODE;
Listagem 3. Lock do tipo Share.
Geralmente o lock S é utilizado para garantir uma imagem consistente da tabela ao
longo da transação, isto é, ele garante que nenhum comando DML será permitido. É
necessário utilizar essa abordagem, pois o isolation Level default do Oracle é Read
Commit, isto é, as alterações efetuadas por outras transações são imediatamente
visíveis para todos os usuários logo após serem “comitadas”. Sendo assim, sua
transação pode ter várias versões da mesma tabela durante sua duração, o que às
vezes não é interessante. Para evitar esse tipo de problema, use um lock S ou inicie a
transação com o isolation level definido para Serializable, ou seja, a transação verá
somente dados “commitados” até o seu inicio.
Atenção:
Quando a tabela está “locada” no modo S, somente a transação que possui o lock pode
executar DMLs. Por se tratar de um lock do tipo share, várias transações podem obtêlo concorrentemente. Caso isso aconteça, nenhuma delas conseguirá efetuar
alterações, pois uma estará bloqueando a outra.
Share Row Exclusive (SSX)
Esse tipo de lock é um pouco mais exclusivo que o S, pois somente uma transação
pode obtê-lo por vez. Ele permite a outras transações obterem locks em linhas
especificas, mas não permite a alteração delas. Esse tipo de lock é obtido através da
instrução da Listagem 4.
LOCK TABLE table IN SHARE ROW EXCLUSIVE MODE;
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
Listagem 4. Lock do tipo Share Row Exclusive.
Exclusive (X)
Esse é o mais restritivo lock existente, ele permite apenas que as outras sessões
acessem a tabela através de instruções SELECTs, ou seja, nenhum tipo de lock é
permitido. Esse lock também é o único lock que pode ser aplicado a linhas. Quando
uma linha está “locada”, ela está “locada” exclusivamente e nenhuma transação
conseguirá alterá-la até que a transação que mantém o lock chegue ao fim através de
um rollback ou commit.
Um deadlock é uma situação que ocorre quando dois processos aguardam para utilizar
um recurso que está sendo utilizado pelo outro processo, ou quando mais de dois
processos estão aguardando por recursos de forma circular. Esta situação pode ocorrer
com qualquer tipo de processos que compartilhem recursos mutuamente exclusivos,
mas é relativamente comum em bancos de dados. O objetivo deste post é clarificar
esta situação e apresentar alguns exemplos.
Durante as modificações de dados, os bancos de dados utilizam bloqueios (locks) para
garantir que outros processos não modifiquem/consultem os mesmos dados
simultaneamente. Quando um processo precisa acessar algum dado que está
bloqueado por outro processo, o primeiro deve aguardar até que o segundo conclua
suas operações. Este bloqueio é normal e necessário, e não caracteriza um deadlock.
Quando o segundo processo concluir suas operações os bloqueios serão liberados e o
primeiro processo continuará executando normalmente. Um deadlock ocorre somente
quando nenhum dos dois (ou mais) processos envolvidos pode continuar sua execução
sem intervenção externa.
Deadlocks também podem ocorrer nos bancos de dados com recursos como memória e
threads, mas o mais comum é envolvendo locks. Normalmente os sistemas
gerenciadores de bancos de dados possuem mecanismos para detectar e resolver
deadlocks.
Abaixo um exemplo simples utilizando o SQL Server. O primeiro processo (tela com
fundo preto) realiza uma alteração no nome do cliente 1, que ocorre normalmente:
Um outro processo (tela com fundo azul) realiza uma alteração no nome do cliente 2,
com sucesso. Em seguida tenta alterar o nome do cliente 1:
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
Neste momento o segundo processo fica bloqueado, pois o cliente 1 está sendo
alterado pelo primeiro processo. Aqui não há deadlock, pois o processo 1 pode concluir
suas alterações (COMMIT/ROLLBACK), liberando assim o processo 2. No entanto, se
neste momento o processo 1 tentar alterar o cliente 2, haverá um deadlock:
O processo 1 estava bloqueando o cliente 1 e esperando para alterar o cliente 2. O
processo 2 estava bloqueando o cliente 2 e esperando para alterar o cliente 1. Ambos
os processos estavam aguardando por um recurso que estava bloqueado pelo outro, e
nenhum dos dois poderia continuar. O software gerenciador de banco de dados
detectou o deadlock e abortou (ROLLBACK) uma das transações (erro 1205 na tela
acima), fazendo com que a outra transação pudesse continuar.
O mecanismo de detecção de deadlocks e as mensagens de erro variam de acordo com
o banco de dados. No Oracle, o seguinte erro é gerado quando um deadlock é
detectado:
ORA-00060: deadlock detected while waiting for resource
Nem todos os gerenciadores de bancos de dados implementam um mecanismo de
detecção de deadlocks. O Progress OpenEdge, por exemplo, não implementa. Em um
banco de dados OpenEdge, quando um usuário fica bloqueado ele recebe uma
indicação visual, informando que o registro está bloqueado por outro usuário:
Pedro F. Carvalho
Analista de Sistemas
[email protected]
www.pedrofcarvlho.com.br
Como não há controle de deadlock, é possível que dois ou mais usuários recebam esta
mesma mensagem, e fiquem um esperando pelo outro.
Existem técnicas para minimizar a ocorrência de deadlocks em bancos de dados.
Normalmente a própria documentação dos bancos de dados contém informações mais
detalhadas sobre o assunto. Consulte sua documentação para mais detalhes
Download