ORA-1555

En la BBDD Oracle, se define que, al ejecutar una sentencia SELECT, los registros que se devolverán serán los que existían en el momento de empezar la sentencia select y con el valor que tenian en este momento.

Esto es cierto independientemente de las modificaciones que haya podido hober en las tablas implicadas y de la duración de la sentencia select.

Para ello, si en una tabla ha habido modificaciones, la BBDD deshace estas modificaciones para poder presentar los valores que habia en el momento de empezar la sentencia select.

Para ello utiliza, principalmente, la información contenida en los Rollback Segments o el Tablespace de Undo. También utiliza parte de la información contenida en la cabecera de los bloques de datos.

El error ORA-1555 (Réplica demasiado antigua - Snapshot too old) se produce cuando la BBDD no es capaz de devolver un registro con el valor que tenia al empezar la sentencia select.

Causas del error ORA-1555

Hemos dado una explicación somera del porqué se produce es error. De ahí se deducen las causas.

Por regla general, las causas son una mezcla de:

  • Sentencia Select que dura mucho tiempo.
  • Plan de acceso basado en índices para esta sentencia.
  • Muchas modificaciones a la BBDD.
  • Muchas modificaciones a la(s) tablas que interviene(n) en la sentencia Select.

Ejemplo

Un ejemplo típico y tópico que puede provocar el error ORA-1555 es una aplicación batch del tipo 'Contabilización de las facturas'

Una aplicación tipo de estas características se basa en un código del tipo:

  • For factura in (SELECT * FROM facturas WHERE contabilizada=false) loop
    • Contabilizar(factura);
    • UPDATE facturas SET contabilizada = true WHERE facturaid=factura.facturaid;
    • COMMIT;
  • end loop;

Obsérvese que, si hay muchas facturas y el proceso de contabilización es pesado, el proceso completo implicará muchas modificaciones a la BBDD.

Como solventar este error

No hay una receta mágica para solventar el error. No obstante, se indican diversas acciones que pueden disminuir la incidencia de error hasta hacerlo desaparecer. En cada caso, debemos analizar cuál de estas posibles soluciones se adapta más a nuestras necesidades.

Aumentar el tamaño de los Rollback Segments o Undo Tablespace

El manejo del espacio de los Rollback Segments (téngase en cuenta que en el Tablespace de UNDO Oracle crea Rollback Segments de forma transparente al usuario) es el siguiente:

  • Mientras el Rollback Segment continene información de una transacción no terminada, el espacio no se libera. Es más: si hace falta, se amplia para poder contener la información de Rollback de la transacción.
  • Una vez terminada la transacción, este espacio se libera (pero conserva la información que tenia).
  • El espacio libre se trata como un buffer FIFO.
    • Con este algoritmo, el espacio libre se mantiene con la información que tenía el máximo tiempo posible.

Una de las causas de este error es que la información de UNDO ya no está disponible en el Rollback Segment o en el Tablespace de UNDO debido, precisamente, a que el espacio que la tenía se ha modificado por alguna transacción.

Si aumentamos el tamaño de los Rollback Segments (o del tablespace de Undo), la información de transacciones terminadas se mantendrá intacta durante más tiempo. Si el tamaño se aumenta de forma suficiente, el error no se producirá.

Reabrir el cursor cuando se produzca este error

Si funcionalmente es posible, una solución es, cuando se detecte este error, volver a lanzar el select.

Es así de sencillo y efectivo.

Forzar una ordenación el la sentencia SELECT original

Este error se produce debido a que la BBDD debe "retroceder" el contenido de la tabla utilizando el contenido de los Rollback Segments.

Si en el select original forzamos una ordenación, la BBDD creará un ResultSet Temporal con los registros a devolver. De esta forma, no precisa ir a la tabla original para devolver la información: la información la va a devolver desde este ResulSet Temporal.

Se debe validar, consultando el Plan de Acceso, que la BBDD realiza un ORDERBY de forma explícita y no utiliza un acceso por índice para ahorrarse el paso de ordenación. No sería raro que el plan de acceso incluyera un FULL SCAN de la tabla principal.

Así, en el ejemplo anterior, la sentencia Select se podrá cambiar por: SELECT * FROM facturas WHERE contabilizada=false ORDER BY importe;