Gestire la richiesta concorrente durante la permanenza nel database Oracle?

Ho questo scenario, su un sito di compagnie aeree (utilizzando Java) due clienti separati inviano due richieste contemporaneamente per prenotare uno stesso posto nella stessa compagnia aerea
da New York a Chicago. Sto usando il database di Oracle e il livello di isolamento viene letto in commit. La mia domanda qui è che il database di Oracle fornisce una soluzione per affrontare questo tipo di scenario concorrente? quello che so è quando viene emessa la prima istruzione DML della transazione che otterrà un blocco sulle righe interessate e verrà rilasciata al completamento della transazione, ad esempio sull’emissione del rollback o del commit.Ma appena viene eseguito il commit e la seconda richiesta verrà eseguita non appena completata la prima e sostituirà il primo. Quindi non aiuta?

Sì, in Java, posso occuparmi di rendere la mia class di db come un singleton e di usare un metodo di parola chiave sincronizzato che sta facendo l’aggiornamento. Ma voglio sapere se esiste comunque questo tipo di problema a livello di database stesso? Probabilmente il livello di isolamento come serializzabile può essere d’aiuto. Ma non sono sicuro?

Per gestire la concorrenza in un sito Web, è prassi comune che una colonna su ogni record che consente di controllarla non sia stata aggiornata da quando l’hai ricevuta. Data dell’ultimo aggiornamento o numero di versione sequenziale (incrementato automaticamente da un trigger).

In genere leggerai i dati (più la colonna della concorrenza)

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

Quindi, quando l’utente alla fine arriva a prenotare il posto, l’aggiornamento funzionerà a meno che non ci sia stato un aggiornamento.

(il numero di versione o la data di aggiornamento cambieranno dopo ogni aggiornamento)

 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; 

il trigger per aggiornare automaticamente il numero di versione sarebbe un po ‘come questo

 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; 

Scriverà solo se lo permetti. Puoi provare qualcosa del genere

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

Ciò restituirà 1 riga aggiornata la prima volta e 0 righe aggiornate successivamente.

Come accennato, le impostazioni di transizione ti aiuteranno a raggiungere una sola operazione. Il modo migliore per imporre questo tipo di restrizioni al fine di garantire che il modello relazionale sia vincolato a non accettare la seconda operazione una volta che il primo ha avuto esito positivo.

Invece di dover eseguire un aggiornamento su una riga, ad esempio update …. seat = “taken”, crea una tabella di prenotazione (customer, flight, seat) che ha un vincolo (colonna: seat = unique) (ricerca ora docs a impara la syntax per quello sulla creazione della tabella). In questo modo il processo di prenotazione diventa un inserto nella tabella delle prenotazioni e puoi fare affidamento sul RDBMS per rafforzare i vincoli relazionali per mantenere valido il tuo modello di business.

Ad esempio, lascia che sia la prima operazione, avrai:

 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. 

L’unico modo per prenotare seat-y nuovo seat-y è quello di rimuovere prima la prenotazione precedente, che probabilmente è ciò che il tuo processo aziendale vuole raggiungere.

Oltre a fare tutto in un singolo UPDATE facendo attenzione alla clausola WHERE , puoi farlo:

Transazione 1:

  • SELECT ... FOR UPDATE blocca esclusivamente la riga per la durata della transazione.
  • Controllare se lo stato restituito della riga è “prenotato” e uscire (o riprovare un’altra riga) se lo è.
  • UPDATE la riga e imposta il suo “stato” su “prenotato” – è garantito che nessun altro lo ha aggiornato nel frattempo.
  • Commettere. Questo rimuove il blocco esclusivo.

Transazione 2:

  • SELECT ... FOR UPDATE blocchi fino al termine della Transazione 1, quindi blocca esclusivamente la riga.
  • Lo stato restituito della riga è “prenotato” (poiché la Transazione 1 lo ha contrassegnato in quel modo), quindi esci (o eventualmente riprova un’altra riga).