Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

O SELECT FOR UPDATE impede a inserção de outras conexões quando a linha não está presente?


MySQL

SELECT ... FOR UPDATE com UPDATE

Usando transações com InnoDB (auto-commit desativado), um SELECT ... FOR UPDATE permite que uma sessão bloqueie temporariamente um determinado registro (ou registros) para que nenhuma outra sessão possa atualizá-lo. Então, dentro da mesma transação, a sessão pode realmente executar um UPDATE no mesmo registro e confirmar ou reverter a transação. Isso permitiria que você bloqueasse o registro para que nenhuma outra sessão pudesse atualizá-lo enquanto talvez você fizesse alguma outra lógica de negócios.

Isso é feito com travamento. O InnoDB utiliza índices para bloquear registros, portanto, bloquear um registro existente parece fácil -- basta bloquear o índice para esse registro.

SELECIONE ... PARA ATUALIZAR com INSERT

No entanto, para usar SELECT ... FOR UPDATE com INSERT , como você bloqueia um índice para um registro que ainda não existe? Se você estiver usando o nível de isolamento padrão de REPEATABLE READ , o InnoDB também utilizará gap fechaduras. Contanto que você saiba o id (ou mesmo intervalo de ids) para bloquear, então o InnoDB pode bloquear a lacuna para que nenhum outro registro possa ser inserido nessa lacuna até terminarmos com isso.

Se o seu id coluna fosse uma coluna de incremento automático, então SELECT ... FOR UPDATE com INSERT INTO seria problemático porque você não saberia qual é o novo id era até você inseri-lo. No entanto, como você conhece o id que você deseja inserir, SELECT ... FOR UPDATE com INSERT vai funcionar.

AVISO

No nível de isolamento padrão, SELECT ... FOR UPDATE em um registro inexistente não bloquear outras transações. Então, se duas transações fizerem um SELECT ... FOR UPDATE no mesmo registro de índice inexistente, ambos obterão o bloqueio e nenhuma transação poderá atualizar o registro. Na verdade, se eles tentarem, um deadlock será detectado.

Portanto, se você não quiser lidar com um impasse, faça o seguinte:

INSERIR EM...

Inicie uma transação e execute o INSERT . Faça sua lógica de negócios e confirme ou reverta a transação. Assim que você fizer o INSERT no índice de registro inexistente na primeira transação, todas as outras transações serão bloqueadas se tentarem INSERT um registro com o mesmo índice exclusivo. Se a segunda transação tentar inserir um registro com o mesmo índice após a primeira transação confirmar a inserção, ela receberá um erro de "chave duplicada". Trate de acordo.

SELECIONAR ... BLOQUEAR NO MODO DE COMPARTILHAMENTO

Se você selecionar com LOCK IN SHARE MODE antes do INSERT , se uma transação anterior inseriu esse registro, mas ainda não foi confirmada, o SELECT ... LOCK IN SHARE MODE bloqueará até que a transação anterior seja concluída.

Portanto, para reduzir a chance de erros de chave duplicada, especialmente se você mantiver os bloqueios por algum tempo enquanto executa a lógica de negócios antes de confirmá-los ou revertê-los:
  1. SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
  2. Se nenhum registro for retornado, então
  3. INSERT INTO FooBar (foo, bar) VALUES (?, ?)