Manipulando a solicitação simultânea enquanto persiste no database Oracle?

Eu tenho esse cenário, em um site de uma companhia aérea (usando Java) dois clientes separados enviam dois pedidos ao mesmo tempo para reservar um mesmo lugar na mesma companhia aérea
de Nova York para Chicago. Eu estou usando o database Oracle e nível de isolamento é lido confirmado. Minha pergunta aqui é que o database Oracle oferece alguma solução para lidar com esse tipo de cenário concorrente? o que eu sei é que quando a primeira instrução DML da transação é acionada, ela obtém um bloqueio nas linhas afetadas e é liberada quando a transação é concluída, ou seja, ao emitir rollback ou commit.But assim que a confirmação é feita e a segunda solicitação continua assim que a primeira é concluída e irá replace o primeiro. Então isso não ajuda?

Sim, em Java eu ​​posso lidar com a minha class db como singleton e usando a palavra-chave sincronizada no método que está fazendo atualização. Mas quer saber se existe de qualquer maneira, podemos este tipo de problema no nível do database em si? Provavelmente nível de isolamento como serializável pode ajudar. Mas não tenho certeza?

Para lidar com a simultaneidade em um site, uma prática comum é ter uma coluna em cada registro que permita que você verifique se ela não foi atualizada desde que você a obteve. Última data de atualização ou um número de versão sequencial (auto incrementado por um acionador).

Normalmente você irá ler os dados (mais a coluna de simultaneidade)

SELECT seat,etc,version_no FROM t1 WHERE column = a_value 

Então, quando o usuário finalmente conseguir reservar o assento, a atualização funcionará, a menos que tenha havido uma atualização.

(o número da versão ou a data de atualização serão alterados após cada atualização)

 BEGIN UPDATE t1 SET seatTaken = true WHERE seatid = ..... AND version_no = p_version RETURNING version_no INTO p_version; EXCEPTION WHEN NOT_FOUND THEN --Generate a custom exception --concurrency viloation the record has been updated already END; 

o gatilho para atualizar automaticamente o número da versão ficaria um pouco parecido com isto

 CREATE OR REPLACE TRIGGER t1_version AFTER INSERT OR UPDATE ON t1 FOR EACH ROW BEGIN IF :new.version_no IS NULL THEN :new.version_no := 0; ELSE :new.version_no := :old.version_no + 1; END IF; END; 

Só vai escrever mais se você permitir. Você pode tentar algo como

 UPDATE seatTable SET seatTaken = true WHERE .. find the seat, flight etc.. AND seatTaken = false 

Isso retornará 1 linha atualizada na primeira vez e 0 linhas atualizadas depois disso.

Como você mencionou, as configurações de transcrição ajudarão você a conseguir uma operação. A melhor maneira de impor este tipo de restrições é garantir que seu modelo relacional seja restrito a não aceitar a segunda operação quando a primeira for bem-sucedida.

Em vez de ter que fazer uma atualização em uma linha, diga update …. seat = “taken”, crie uma tabela de reservas (customer, flight, seat) que tenha uma restrição (column: seat = unique) (pesquisa ou docs para aprenda a syntax para isso na criação da tabela). Dessa forma, seu processo de reserva se torna uma inserção na tabela de reservas e você pode confiar no RDBMS para impor suas restrições relacionais para manter seu modelo de negócios válido.

Por exemplo, se t1 for o tempo de operação anterior, você terá:

 t1=> insert into reservations(customer1,flight-x,seat-y) // succeeds. Customer 1 reserved the seat-y t2=> insert into reservations(customer2,flight-x,seat-y) // fails with RDBMS unique constrain violated. 

A única maneira de reservar o seat-y novamente é primeiro remover a reserva anterior, que é provavelmente o que seu processo de negócios deseja alcançar.

Além de fazer tudo em um único UPDATE , criando cuidadosamente a cláusula WHERE , você pode fazer isso:

Transação 1:

  • SELECT ... FOR UPDATE bloqueia exclusivamente a linha pela duração da transação.
  • Verifique se o status retornado da linha está “reservado” e saia (ou tente outra linha), se estiver.
  • UPDATE a linha e defina seu “status” para “reservado” – é garantido que ninguém mais a atualizou nesse meio tempo.
  • Commit. Isso remove o bloqueio exclusivo.

Transação 2:

  • SELECT ... FOR UPDATE blocos até que a Transação 1 termine, depois bloqueie exclusivamente a linha.
  • O status retornado da linha é “reservado” (já que a Transação 1 o marcou dessa maneira), então saia (ou tente novamente outra linha).