Domanda

sto lavorando su una web app collegata a Oracle. Abbiamo una tabella in Oracle con una colonna "attivato". Solo una riga può avere questa colonna impostato a 1 in qualsiasi momento. Per applicare questo, abbiamo usato livello di isolamento SERIALIZZATA in Java, ma ci sono in esecuzione nell'errore "non può serializzare transazione", e non possiamo capire perché.

Ci chiedevamo se un livello di isolamento di Read Committed avrebbe fatto il lavoro. Quindi la mia domanda è questa:

Se abbiamo una transazione che coinvolge il seguente SQL:

SELECT *
FROM MODEL;

UPDATE MODEL
SET ACTIVATED = 0;

UPDATE MODEL
SET ACTIVATED = 1
WHERE RISK_MODEL_ID = ?;

COMMIT;

Dato che è possibile per più di uno di tali operazioni essere in esecuzione allo stesso tempo, sarebbe possibile per più di una riga MODEL per avere il flag attivato impostato a 1?

Qualsiasi aiuto sarebbe apprezzato.

È stato utile?

Soluzione

la soluzione dovrebbe funzionare: il tuo primo aggiornamento si blocca l'intera tabella. Se un'altra transazione non è finito, l'aggiornamento attenderà. Il tuo secondo aggiornamento garantirà che solo una riga avrà il valore 1 perché stai fissando il tavolo (non impedisce tuttavia istruzioni INSERT).

Si dovrebbe anche fare in modo che la riga con il RISK_MODEL_ID esiste (o avrete a zero riga con il valore '1' alla fine della transazione).

Per evitare che le istruzioni INSERT concorrenti, si farebbe BLOCCO tabella (in modalità esclusiva).

Altri suggerimenti

Si potrebbe utilizzare un unico, indice basato funzione di lasciar Oracle gestire il vincolo di avere solo una sola riga con bandiera attivato impostato a 1.

CREATE UNIQUE INDEX MODEL_IX ON MODEL ( DECODE(ACTIVATED, 1, 1, NULL));

Questo fermerebbe più riga avente il flag è impostato su 1, ma non significa che c'è sempre una fila con il flag impostato a 1.

Se quello che vuoi è quello di garantire che solo una transazione può essere eseguito in un momento quindi è possibile utilizzare la sintassi FOR UPDATE. Come si ha una singola riga che necessita di bloccaggio questo è un approccio molto efficiente.

declare
    cursor c is 
        select activated
        from model
        where activated = 1
        for update of activated;
    r c%rowtype;
begin
    open c;
    --  this statement will fail if another transaction is running
    fetch c in r;
    ....
    update model
    set activated = 0
    where current of c;

    update model
    set activated = 1
    where risk_model_id = ?;

    close c;

    commit;
end;
/

Il commit libera la serratura.

Il comportamento di default è di aspettare fino a quando viene liberata la riga. In caso contrario, siamo in grado di specificare NOWAIT, nel qual caso qualsiasi altra sessione di tentare di aggiornare la riga attiva corrente non riuscirà subito, o possiamo aggiungere un'opzione WAIT con un tempo di polling. NOWAIT è la possibilità di scegliere di evitare assolutamente il rischio di impiccagione, e ci dà anche la possibilità di informare l'utente che qualcun altro sta aggiornando il tavolo, che si potrebbe desiderare di sapere.

Questo approccio è molto più scalabile rispetto aggiornare tutte le righe della tabella. Utilizzare un indice basato su una funzione come mostrato WW applicare la regola che solo una riga può essere attivata = 1.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top