gerenciando dados e concorrÊncia...

24
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

Upload: lykiet

Post on 01-Jul-2018

213 views

Category:

Documents


0 download

TRANSCRIPT

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

Recria a trigger se esta já existir.

schema 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

REFERENCING

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 EACH ROW

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.

pl/sql_block 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:

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

BEFORE Se a trigger for disparada BEFORE os dados podem ser alterados.

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.

:NEW.Nome_do_Campo 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.

AFTER Se a trigger for disparada AFTER os dados não podem ser alterados.

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.

BEFORE

Se a operação for UPDATE retornará o valor do campo antes da alteração. No DELETE retornará o conteúdo do campo atual.

:OLD.Nome_do_Campo Retorna o conteúdo antigo do campo e nunca pode ser alterado. No INSERT não é utilizado pois não há valores antigos a inserção.

AFTER

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

Constraints

Gravação Triggers

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;

1 row updated.

08:00 UPDATE PRODUTO SET valor = 50 WHERE id_produto = 2;

1 row updated.

UPDATE produto SET valor = 52 WHERE id_produto = 2;

Nenhuma linha atualizada no momento, pois a transação 1 entra em lock conflict com a transação 2.

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;

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 user usr1 2 profile default 3 identified by oracle 4 quota unlimited on users 5 account unlock;

Usuário criado.

SQL> create user usr2 2 profile default 3 identified by oracle 4 quota unlimited on users 5 account unlock;

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> create table produto 2 ( 3 id_produto number primary key, 4 valor number(10,2) 5 ) 6 /

Tabela criada.

SQL> insert into produto values (&id, &valor); Informe o valor para id: 1 Informe o valor para valor: 50

1 linha criada.

SQL> / Informe o valor para id: 2 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 produto set valor = 35 2 where id_produto = 1;

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> update usr1.produto set valor = 36 2 where id_produto = 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 where sid in ( 3 select blocking_session from v$session 4 ) 5 /

USERNAME ------------------------------ USR1

Verificando usuários que estão aguardando commit de outra sessão. SQL> select sid, username, blocking_session 2 from v$session 3 where blocking_session is not null;

SID USERNAME BLOCKING_SESSION ----- ------------ ---------------- 136 USR2 141

Usuários que estão com o lock, e usuário que estão aguardando a outra transação.

SQL> select sid, username, blocking_session 2 from v$session where blocking_session is not null 3 UNION ALL 4 select A.sid, A.username, A.blocking_session 5 from v$session A, v$session B 6 where A.sid = B.blocking_session;

SID USERNAME BLOCKING_SESSION ---- ---------- ---------------- 136 USR2 141 141 USR1

Resolvendo lock conflicts.

Somente é possível resolver conflitos de bloqueios através de duas maneiras:

1) Emitir um commit na sessão que está bloqueando 2) Terminar a 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 sid, serial# 2 from v$session where username = 'USR1';

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 Level Row Share (RS) Tabela Row Excluive (RX) Tabela Share (S) Tabela Share Row Exclusive (SSX) Tabela Share Row Exclusive (SRX) Tabela Exclusive (X) 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