2010-11-19 22 views
16

Tengo un par de instancias de una aplicación J2EE ejecutándose en un único clúster de WebLogic.¿Puedo hacer una MERGE atómica en Oracle?

En algún momento, estas aplicaciones hacen un MERGE para insertar o actualizar un registro en la base de datos de Oracle de fondo. El MERGE comprueba si hay una fila con una clave primaria especificada o no. Si está allí, actualiza. Si no, inserte.

Supongamos ahora que dos instancias de aplicación desean insertar o actualizar una fila con la clave principal = 100. Supongamos que la fila no existe. Durante la etapa de "comprobación" de fusión, ambos ven que las filas no están allí, por lo que ambos intentan insertar. Luego recibo una violación de restricción clave única.

Mi pregunta es esta: ¿Hay una MERGE atómica en Oracle? Estoy buscando algo que tenga un efecto similar al INSERT ... FOR UPDATE en PL/SQL, excepto que solo puedo ejecutar SQL desde mis aplicaciones.

EDIT: No estaba claro. ESTOY usando la instrucción MERGE mientras este error aún ocurre. La cuestión es que solo la parte "modificadora" es atómica, no la combinación completa.

+2

La combinación es atómica. Funciona o falla como una unidad de trabajo completa. Lo que está viendo es el resultado de la implementación de Oracle de la consistencia multiversion. ¿Parece que estás buscando algo para seralizar las fusiones múltiples? Puede intentar usar las transacciones seralizables de Oracle, pero eso probablemente solo cambie el error de la restricción de clave única a no poder serializar el error de transacción. –

+0

Mi base de datos/vocabulario de múltiples hilos podría estar equivocado. Mi comprensión es que el "trabajo o falla como una unidad de trabajo completa" se llama transaccional. Por Atomic, me refiero a que no puede producirse una fusión mientras se procesa otra combinación. Acerca de la serialización de fusiones, tendré que leer sobre eso. Gracias. – Russell

+1

@Russell, la atomicidad es solo una propiedad de una transacción. Y significa tener éxito o fracasar como una unidad. Para ver exapmle, consulte: http://en.wikipedia.org/wiki/ACID y http://download.oracle.com/docs/cd/E11882_01/server.112/e16508/transact.htm#sthref1298. –

Respuesta

6

La instrucción MERGE en la segunda sesión no puede "ver" la inserción que la primera sesión hizo hasta que la sesión se compromete. Si reduce el tamaño de las transacciones, se reducirá la probabilidad de que esto ocurra.

O bien, puede ordenar o particionar sus datos para que todos los registros de una clave principal determinada se otorguen a la misma sesión. Una función simple como "clave principal mod N" debe distribuirse uniformemente a N sesiones.

Por cierto, si dos registros tienen la misma clave principal, el segundo sobrescribirá el primero. Suena un poco extraño.

+0

+1 para la solución de nivel de aplicación. Gracias. – Russell

+2

Esto no responde la pregunta. Y la solución alternativa no funcionará si desea sincronizar dos aplicaciones separadas teniendo solo el db como un elemento común. – Dariusz

3

Sí, y se llama .... MERGE

EDITAR: La única manera de conseguir esto es a prueba de agua para insertar, detectar la excepción DUP_VAL_ON_INDEX y manejarlo adecuadamente (actualizar o insertar otro registro quizá) Esto se puede hacer fácilmente con PL/SQL, pero no puede usar eso.

También está buscando soluciones provisionales. ¿Puedes coger el dup_val_on_index en Java y emitir una ACTUALIZACIÓN adicional de nuevo?

En pseudo-código:

try { 
    // MERGE 
} 
catch (dup_val_on_index) { 
    // UPDATE 
} 
+0

Hahaha gracias :) He editado la pregunta para aclaración. – Russell

+0

¡Bien, lo investigaré un poco más después de mi fin de semana! –

+0

¿Eres capaz de usar PL/SQL? Lo primero que encontré sugiere que un inserto con manejo de excepción (cuando dup_val_on_index then update) es la única forma de obtener esta agua estanca. –

2

me sorprende que MERGE se comportaría la forma en que describes, pero yo no lo he utilizado lo suficiente como para decir si se debe o no.

En cualquier caso, es posible que las transacciones que desean ejecutar la fusión establezcan su nivel de aislamiento en SERIALIZABLE. Creo que eso puede resolver tu problema.

13

Esto no es un problema con MERGE como tal. Más bien, el problema radica en su aplicación. Considere este procedimiento almacenado:

create or replace procedure upsert_t23 
    (p_id in t23.id%type 
     , p_name in t23.name%type) 
is 
    cursor c is 
     select null 
     from t23 
     where id = p_id; 
    dummy varchar2(1); 
begin 
    open c; 
    fetch c into dummy; 
    if c%notfound then 
     insert into t23 
      values (p_id, p_name); 
    else 
     update t23 
      set name = p_name 
      where id = p_id; 
    end if; 
end; 

Entonces, este es el equivalente PL/SQL de un MERGE en T23. ¿Qué sucede si dos sesiones lo llaman simultáneamente?

SSN1> exec upsert_t23(100, 'FOX IN SOCKS') 

SSN2> exec upsert_t23(100, 'MR KNOX') 

SSN1 llega primero, no encuentra ningún registro coincidente e inserta un registro. SSN2 llega al segundo lugar pero antes de que SSN1 se comprometa, no encuentre ningún registro, inserta un registro y cuelga porque SSN1 tiene un bloqueo en el nodo de índice único para 100. Cuando SSN1 confirma SSN2 lanzará una violación DUP_VAL_ON_INDEX.

La instrucción MERGE funciona exactamente de la misma manera. Ambas sesiones verificarán on (t23.id = 100), no lo encontrarán y pasarán a la rama INSERTAR. La primera sesión tendrá éxito y la segunda lanzará ORA-00001.

Una forma de manejar esto es introducir un bloqueo pesimista.En el inicio del procedimiento UPSERT_T23 cerramos la tabla:

... 
lock table t23 in row shared mode nowait; 
open c; 
... 

Ahora, SSN1 llega, coge el bloqueo y procede como antes. Cuando llega el SSN2, no puede obtener el bloqueo, por lo que falla inmediatamente. Lo cual es frustrante para el segundo usuario, pero al menos no están colgando, además saben que alguien más está trabajando en el mismo registro.

No hay sintaxis para INSERT que es equivalente a SELECCIONAR ... PARA ACTUALIZAR, porque no hay nada que seleccionar. Y entonces tampoco existe tal sintaxis para MERGE. Lo que debe hacer es incluir la instrucción LOCK TABLE en la unidad de programa que emite MERGE. Si esto es posible para ti depende del marco que estás usando.

+1

Gracias, esa fue la articulación perfecta de lo que estaba tratando de explicar en mi pregunta. Acerca de cómo manejaría el problema, ¿no es parte de bloqueo de PL/SQL? Estoy buscando una solución/solución utilizando SQL de Oracle que se puede llamar desde mi aplicación. Realmente no estaba buscando un INSERTO que sea equivalente a SELECCIONAR ... PARA ACTUALIZAR (aunque la forma en que lo indiqué podría haberlo llevado a pensar de esa manera).Solo estaba descubriendo si había alguna forma de bloquear la fila fusionada mediante una sentencia MERGE (de ahí el título de la pregunta). De todos modos, gracias por la respuesta completa y el conocimiento :) – Russell

+1

Bloquear una tabla en el recurso compartido en la fila (no en la fila compartido) no logra casi nada. Evitará que otra sesión obtenga un bloqueo de tabla exclusivo. No evitará que otra sesión se actualice/inserte/elimine. Por cierto, el bloqueo de compartir de la fila se adquiere automáticamente cuando emite una declaración de "seleccionar ... para la actualización". El modo "Share Row Exclusive" evitará que otras sesiones cambien la tabla y no podrá adquirirlo si otra sesión tiene cambios no confirmados. Esto es casi tan reparador como un bloqueo exclusivo. Su uso evitará todas las actualizaciones simultáneas, como un sistema de "usuario único". – redcayuga

Cuestiones relacionadas