Oracle
 sql >> Database >  >> RDS >> Oracle

SELEZIONA PER AGGIORNAMENTO impedisce l'inserimento di altre connessioni quando la riga non è presente?

MySQL

SELEZIONARE... PER AGGIORNARE con AGGIORNAMENTO

Usando le transazioni con InnoDB (commit automatico disattivato), un SELECT ... FOR UPDATE consente a una sessione di bloccare temporaneamente uno o più record particolari in modo che nessun'altra sessione possa aggiornarlo. Quindi, all'interno della stessa transazione, la sessione può effettivamente eseguire un UPDATE sullo stesso record e eseguire il commit o il rollback della transazione. Ciò ti consentirebbe di bloccare il record in modo che nessun'altra sessione possa aggiornarlo mentre forse esegui qualche altra logica aziendale.

Questo si ottiene con il blocco. InnoDB utilizza gli indici per bloccare i record, quindi bloccare un record esistente sembra facile:blocca semplicemente l'indice per quel record.

SELEZIONA... PER AGGIORNARE con INSERT

Tuttavia, per utilizzare SELECT ... FOR UPDATE con INSERT , come si blocca un indice per un record che non esiste ancora? Se stai usando il livello di isolamento predefinito di REPEATABLE READ , InnoDB utilizzerà anche gap serrature. Se conosci l'id (o anche un intervallo di ID) da bloccare, quindi InnoDB può bloccare il divario in modo che nessun altro record possa essere inserito in quel divario finché non abbiamo finito con esso.

Se il tuo id erano una colonna con incremento automatico, quindi SELECT ... FOR UPDATE con INSERT INTO sarebbe problematico perché non sapresti quale sia il nuovo id era fino a quando non l'hai inserito. Tuttavia, poiché conosci l'id che desideri inserire, SELECT ... FOR UPDATE con INSERT funzionerà.

AVVISO

Al livello di isolamento predefinito, SELECT ... FOR UPDATE su un record inesistente non bloccare altre transazioni. Quindi, se due transazioni eseguono entrambe un SELECT ... FOR UPDATE sullo stesso record di indice inesistente, entrambi riceveranno il blocco e nessuna delle due transazioni sarà in grado di aggiornare il record. Infatti, se ci provano, verrà rilevato un deadlock.

Pertanto, se non vuoi affrontare un deadlock, puoi semplicemente fare quanto segue:

INSERIRE IN ...

Avvia una transazione ed esegui il INSERT . Esegui la tua logica aziendale e esegui il commit o il rollback della transazione. Non appena esegui il INSERT sull'indice del record inesistente sulla prima transazione, tutte le altre transazioni si bloccheranno se tentano di INSERT un record con lo stesso indice univoco. Se la seconda transazione tenta di inserire un record con lo stesso indice dopo che la prima transazione ha eseguito il commit dell'inserimento, riceverà un errore di "chiave duplicata". Gestisci di conseguenza.

SELEZIONA... BLOCCA IN MODALITÀ CONDIVISIONE

Se selezioni con LOCK IN SHARE MODE prima del INSERT , se una transazione precedente ha inserito quel record ma non ha ancora eseguito il commit, il SELECT ... LOCK IN SHARE MODE si bloccherà fino al completamento della transazione precedente.

Quindi, per ridurre la possibilità di errori di chiave duplicati, soprattutto se tieni premuti i blocchi per un po' mentre esegui la logica aziendale prima di commetterli o ripristinarli:

  1. SELECT bar FROM FooBar WHERE foo = ? LOCK FOR UPDATE
  2. Se non vengono restituiti record,
  3. INSERT INTO FooBar (foo, bar) VALUES (?, ?)