2009-09-22 9 views
5

Estoy trabajando en una aplicación web conectada a Oracle. Tenemos una tabla en el oráculo con una columna "activada". Solo una fila puede tener esta columna establecida en 1 en cualquier momento. Para hacer cumplir esto, hemos estado utilizando el nivel de aislamiento SERIALIZED en Java, sin embargo, nos encontramos con el error "no se puede serializar la transacción", y no podemos determinar por qué.LEA el nivel de aislamiento de la base de datos COMPROMETIDO en el oráculo

Nos preguntábamos si un nivel de aislamiento de READ COMMITTED haría el trabajo. Así que mi pregunta es la siguiente:

Si tenemos una transacción que implica el siguiente SQL:

SELECT * 
FROM MODEL; 

UPDATE MODEL 
SET ACTIVATED = 0; 

UPDATE MODEL 
SET ACTIVATED = 1 
WHERE RISK_MODEL_ID = ?; 

COMMIT; 

Dado que es posible que más de una de estas transacciones a ejecutar al mismo tiempo, ¿verdad ¿Será posible que más de una fila de MODELO tenga el indicador activado en 1?

Cualquier ayuda sería apreciada.

Respuesta

3

su solución debería funcionar: su primera actualización bloqueará toda la tabla. Si no se completa otra transacción, la actualización esperará. Su segunda actualización garantizará que solo una fila tendrá el valor 1 porque está bloqueando la tabla (sin embargo, no impide las instrucciones INSERT).

También debe asegurarse de que la fila con RISK_MODEL_ID exista (o tendrá una fila cero con el valor '1' al final de su transacción).

Para evitar declaraciones INSERT simultáneas, debería LOCK la tabla (en MODO EXCLUSIVO).

+0

similares, pero me gustaría cerradura Modelo en modo exclusivo y hacer una actualización del modelo SET ACTIVADO = 0 donde activada = 1 se actualizan Menos filas, por lo que es un poco más performante, y la tabla de bloqueos impediría la actividad simultánea en la mesa. –

3

Se podría considerar el uso de un índice basado exclusivo, funciones para que Oracle manejar la restricción de tener sólo una fila con una bandera activada se establece en 1.

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

Esto dejaría más de una fila que tiene la bandera se establece en 1, pero no significa que siempre hay una fila con el indicador establecido en 1.

+0

+1: caja fuerte simple, eficiente y multiusuario –

2

Si lo que desea es garantizar que solo se pueda ejecutar una transacción a la vez, puede usar la sintaxis FOR UPDATE. Como tiene una sola fila que necesita bloquearse, este es un enfoque muy eficiente.

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; 
/

El commit libera la cerradura.

El comportamiento predeterminado es esperar hasta que se libere la fila. De lo contrario, podemos especificar NOWAIT, en cuyo caso cualquier otra sesión que intente actualizar la fila activa actual fallará de inmediato, o podemos agregar una opción WAIT con un tiempo de sondeo. NOWAIT es la opción para elegir evitar por completo el riesgo de bloqueo, y también nos da la oportunidad de informar al usuario que alguien más está actualizando la tabla, lo cual es posible que desee saber.

Este enfoque es mucho más escalable que la actualización de todas las filas de la tabla. Use un índice basado en funciones como WW mostró para hacer cumplir la regla de que solo una fila puede tener ACTIVADO = 1.

+0

+1: buena solución –

Cuestiones relacionadas