2012-08-06 19 views
7

Me he encontrado con un problema extraño últimamente, programando en una base de datos Oracle: dentro de una transacción serializable, hago una inserción masiva (INSERTAR ... SELECCIONAR), e inmediatamente después, I abra un cursor con un SELECCIONAR en la tabla alterada. Supuse que este cursor incluiría las filas recién insertadas, pero, para mi sorpresa, su contenido es errático, a veces incluye todas las filas recién insertadas, y algunas veces solo un subconjunto.Oracle: seleccione inmediatamente después de la inserción en la transacción serializable

He resuelto este problema al comprometerme antes de abrir el cursor, pero el comportamiento me ha intrigado. ¿Se puede confiar en una selección después de una inserción dentro de la misma transacción, sin un compromiso de intervinculación? ¿O está este comportamiento relacionado de alguna manera con la transacción que se puede serializar?

Seguimiento: Al intentar crear un caso de prueba reproducible, solo pude obtener este comportamiento una vez que agregué un índice (en este caso un índice de clave principal, en el código real era un índice normal). Tal vez el problema radica en el tiempo invertido en la creación del índice, de modo que SELECT en realidad utiliza un índice incompleto para recuperar los resultados. De todos modos, aquí va un caso de prueba reproducible:

-- Create empty source table 
CREATE TABLE TEST_CASE_1 AS 
    (SELECT 'CONTENT' AS CONTENT 
    FROM DUAL 
    WHERE 1 = 2) 

-- Add primary key 
ALTER TABLE TEST_CASE_1 
ADD CONSTRAINT TEST_CASE_1_PK PRIMARY KEY (CONTENT); 

-- Create empty destination table 
CREATE TABLE TEST_CASE_2 AS 
    (SELECT 'CONTENT' AS CONTENT 
    FROM DUAL 
    WHERE 1 = 2) 

-- Example of faulty code 
BEGIN 

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 

    -- Populate with 100.000 rows (I used ALL_OBJECTS but any source of 100.000 rows is good) 
    INSERT INTO TEST_CASE_1 
    (SELECT ROWNUM 
    FROM ALL_OBJECTS 
    WHERE ROWNUM <= 100000); 

    INSERT INTO TEST_CASE_2 
    (SELECT * 
    FROM TEST_CASE_1 
    WHERE CONTENT > 0); 

    COMMIT; 

END; 

En este ejemplo, yo esperaría TEST_CASE_2 de contar también con 100.000 filas. Al reproducir este caso de prueba (en una base de datos sin carga), obtuve alrededor de 400-500 filas insertadas. Al eliminar la declaración que establece la transacción como serializable, obtuve el conteo correcto de 100.000 filas.

+0

¿Está abriendo el cursor en la misma sesión de la base de datos que está haciendo la inserción? ¿Dónde está haciendo este trabajo, en realidad en la base de datos o desde una aplicación cliente, y si este último tiene agrupación de conexiones, y usted (a veces) obtiene una conexión diferente para las dos acciones? –

+0

El cursor se abre en la misma sesión. Originalmente, el cursor se abrió en la base de datos y luego se repitió en un ejecutable de .NET, pero para aislar el problema hice una versión del procedimiento que iteraba el cursor dentro de la base de datos, y el problema persistía; de hecho, antes de hacer esto no podía Me convencí a mí mismo de que el problema estaba en la selección siguiendo el inserto. – user1578874

+0

Eso no debería suceder; desde [los documentos] (http://docs.oracle.com/cd/E11882_01/server.112/e25789/consist.htm#sthref1189) 'En el nivel de aislamiento de serialización, una transacción solo ve los cambios comprometidos en el momento de la transacción -no comienza la consulta y los cambios realizados por la transacción en sí ", por lo que debería ver sus propios cambios. Podría ser un error en tu versión específica, supongo. Comprometerse hace que cambiar el nivel de aislamiento sea un poco inútil. ¿Puedes publicar un caso de prueba reproducible? –

Respuesta

8

Esto parece ser un error; si tiene acceso al sitio web de soporte de Oracle, mire la nota 1455175.1, que data de 8i. Hay un par de números de error enumerados (7592038 - 'DATOS SILENCIOSAMENTE INVISIBLES DESDE SELECCIONAR/ACTUALIZAR DE FILA RECIENTEMENTE INSERTADA EN SERIALIZABLE', 6363019) pero están cerrados como duplicados de 440317 ('NIVELES DE AISLAMIENTO SERIALIZABLES CAUSA QUE NO SE ENCUENTREN DATOS EN FILAS SELECCIONADOS DESPUÉS DE INSERTAR '), que se muestra como aún abierto y está siendo investigado por desarrollo, incluso si se originó originalmente en la versión 7 (!).

Parece que tiene razón, está relacionado con el PK. Las soluciones alternativas enumeradas son:

  • Confirmar el trabajo ejecutado hasta ese momento.
  • Ejecutar declaraciones adicionales (pero diferentes) (quizás después de volver a un punto de rescate establecido anteriormente en la transacción).
  • Deshaga la transacción completa y reinicie la transacción desde el principio.
  • Realice un escaneo completo de la tabla y evite usar los índices.

¿Sabía que la primera solución ya es efectiva, y no creo que la segunda o la tercera lo ayuden? Puede intentar el cuarto, agregando una sugerencia de /*+ FULL(TEST_CASE_1) */ a la selección para el segundo inserto.

No obtengo el error en 11.2.0.2 (Linux), aunque no puedo encontrar nada que sugiera que el error haya sido reparado; y no tengo un entorno 11.1 para probarlo, así que no puedo verificar que el último trabajo se aplique a este caso de prueba.

Hay una nota de que puede obtener ORA-08177 en lugar de 11G. Tenía ese problema si ejecutaba el bloque anónimo demasiado pronto después de crear las tablas, o si tenía demasiadas filas insertadas, lo que también parece estar relacionado con el PK. This previous question puede ser relevante.

Parece que esto seguirá siendo un problema, por lo que si las soluciones no lo ayudan, es posible que deba reconsiderar si realmente necesita cambiar el nivel de aislamiento; y si lo hace, es posible que tenga que plantear una solicitud de servicio con Oracle para obtener una mejor respuesta.

+0

¡Muchas gracias por su minuciosa respuesta! El error que estoy experimentando parece corresponder al problema en el soporte de Oracle. Me alegra que sea un error ya que dependemos de transacciones serializables para una sección muy pequeña pero crítica para la misión de nuestro sistema, y ​​si se tratara de una semántica estándar para transacciones serializables, probablemente tendríamos que reescribir toda la sección.La primera solución (que utilicé) está bien en mi escenario particular, pero no en otras partes del programa. Mantendré todo el arsenal de soluciones en mi caja de herramientas. – user1578874

+1

Parece que este error * aún * no es fijo; Acabo de reproducirlo con Oracle 12.1.0.1.0 en Windows. –

+1

Acabo de recibirlo en Oracle 12.1.0.2.0 - Linux de 64 bits ... – Jakob

2

Esto es un error confirmado, y Oracle declaró que no planean solucionarlo. He aquí un extracto de su respuesta a mi solicitud de servicio (enero de 2015):

Estos síntomas se deben a la transacción serializable ha encontrado con los problemas conocidos y que la conclusión con el Bug 440317 es correcta.

Bug 440317 - nivel de aislamiento CAUSAS SERIALIZABLE No se encontraron datos en las filas seleccionadas después de INSERT
Bug 16803610 - filas insertadas USO INSERT INTO se pierden en AISLAMIENTO SERIALIZABLE TRANSAC NIVEL

Ambos estos insectos se publican, por lo que se puede ver los detalles en la búsqueda de errores MOS .

Según el desarrollo, hubo varios errores para el mismo problema con muy larga historia. El diseño no es fácil de cambiar, por lo tanto, no hay una solución hasta el momento en que esta característica no sea muy útil.

El desarrollo ha cerrado el error diciendo que la corrección de código no es factible.

Las soluciones sugeridas son
código de aplicación modifiction:
cambio de la lógica de tener el commit antes del selecto
o literatura no toman serializable
sin modificaciones código de aplicación:
No utilizar la clave principal o los índices en la tabla

Cuestiones relacionadas