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.
This is a translation of the MySQL Reference Manual that can be found at dev.mysql.com. The original Reference Manual is in English, and this translation is not necessarily as up to date as the English version.