LOCK IN SHARE MODE


Uma leitura consistente não é conveniente em alguma circunstâncias. Suponha que você queira adicionar uma nova linha em sua tabela CHILD, e está certo que ela já possui um pai na tabela PARENT.

Suponha que você utilize leitura consistente para ler a tabela PARENT e certamente veja o pai do filho na tabela. Agora você pode adiciona com segurança o registro filho na tabela CHILD? Não, porque pode ter acontecido de outro usuário ter deletado o registro pai da tabela PARENT, e você não estar ciente disto.

A solução é realizar o SELECT em um modo de travamento, LOCK IN SHARE MODE.

SELECT * FROM PARENT WHERE NAME = 'Jones' LOCK IN SHARE MODE;

Realizar uma leitura em modo compartilhado significa que lemos o dado disponível por último e configuramos travas de leitura nos registros lidos. Se o este dado pertencer a uma transação de outro usuário que ainda não fez commit, esperaremos até que o commit seja realizado. Uma trava em modo compartilhado previne que ocorra atualizações ou deleções de registros já lidos. Depois de vermos que a consulta acima retornou o pai 'Jones', podemos com segurança adicionar o seu filho a tabela CHILD, e realizar o commit de nossa transação. Este exemplo mostra como implementar integridade referêncial no código de sua aplicação.

Deixe-nos mostrar outro exemplo: temos um compo de contador inteiro em uma tabela CHILD_CODES que usamos para atribuir um identificador único para cada filho que adicionamos na tabela CHILD. Obviamente, usar uma leitura consistente ou uma leitura em modo compartilhado para ler o valor atual do contador não é uma boa idéia, já que dois usuários do banco de dados podem ver o mesmo valor para o contador e, assim, teríamos um erro de chave duplicada ao adicionarmos os dois filhos com o mesmo identificador para a tabela.

Neste caso existem dois bons modos de se implementar a leitura e o incremento do contador: (1) atualizar o contador primeiro aumentando-o de 1 e só depois disto lê-lo, ou (2) ler o contador primeiro com um modo de bloqueio FOR UPDATE, e incrementá-lo depois disto:

SELECT COUNTER_FIELD FROM CHILD_CODES FOR UPDATE;
UPDATE CHILD_CODES SET COUNTER_FIELD = COUNTER_FIELD + 1;

Um SELECT ... FOR UPDATE irá ler o dado disponível por último atribuindo travas exclusivas a cada linha que ele ler. Assim ele atribui uma mesma trava que um UPDATE SQL pesquisado atribuiria nos registros.

Retornar