2009-03-19 13 views
5

Estamos experimentando un error Oracle Deadlock (org.hibernate.util.JDBCExceptionReporter - ORA-00060: interbloqueo detectado mientras se espera el recurso). Se ha sugerido que el problema está en un proceso que está realizando operaciones de solo lectura usando Hibernate mientras que otro proceso está realizando una actualización en la misma fila.Oracle Deadlock cuando la aplicación Hibernate carga datos para uso de solo lectura

El proceso de solo lectura en cuestión se configura con Hibernate y Spring. No hemos definido explícitamente una transacción para el servicio. Si bien eso puede no ser el ideal, no veo por qué Hibernate intentaría obtener un bloqueo exclusivo en una fila cuando no se realizaban operaciones de guardado/actualización, solo un get/load.

Así que mi pregunta es: ¿Hibernate, cuando no se define una gestión explícita de transacciones, intenta obtener un bloqueo de lectura/escritura en una fila incluso si solo se realiza una "carga" de un objeto. No se realiza ninguna operación de guardar/actualizar.

¿Es posible que definir una transacción alrededor del servicio que está cargando datos y decir específicamente READONLY en transactionAttributes haga que Hibernate ignore un bloqueo de fila ya existente y simplemente cargue los datos con fines de solo lectura?

Éstos son algunos ejemplos de código:

para cargar el registro estamos utilizando un HibernateDaoTemplate

public class HibernatePurchaseOrderDataService extends HibernateDaoSupport implements PurchaseOrderDataService { 
    public PurchaseOrderData retrieveById(Long id) { 
     return (PurchaseOrderData)getHibernateTemplate().get(PurchaseOrderData.class, id); 
    } 
} 

La configuración de la primavera para el servicio de llamar a este método es:

<bean id="orderDataService" 
     class="com.example.orderdata.HibernatePurchaseOrderDataService"> 
    <property name="sessionFactory" ref="orderDataSessionFactory"/> 
</bean> 

<bean id="orderDataSessionFactory" 
     class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
    <property name="dataSource" ref="hibernateDataSource"/> 
    <property name="hibernateProperties" ref="hibernateProperties"/> 
    <property name="mappingResources"> 
     <list> 
      <value>com/example/orderdata/PurchaseOrderData.hbm.xml</value> 
      <value>com/example/orderdata/PurchaseOrderItem.hbm.xml</value> 
     </list> 
    </property> 
</bean> 

El actual se produce un interbloqueo en uno de los registros PurchaseOrderItem cargados por la llamada a la carga de PurchaseOrder.

¿Causaría esto un punto muerto si el registro cargado estuviera bloqueado por otro proceso? Y en caso afirmativo, ¿sería útil resolver el problema al agregar un contenedor de transacciones como el que se muestra a continuación?

<bean id="txWrappedOrderDataService" 
     class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 
    <property name="transactionManager" ref="transactionManager"/> 
    <property name="target" ref="orderDataService"/> 
    <property name="transactionAttributes"> 
    <props> 
     <!-- all methods require a transaction --> 
     <prop key="*">PROPAGATION_REQUIRED,readOnly</prop> 
    </props> 
    </property> 
</bean> 

Actualización: El equipo de base de datos ha visto los mensajes de rastreo en el servidor que parecen indicar que nuestro proceso de "sólo lectura" es en realidad escribiendo a la base de datos de forma automática. Hay comandos registrados de "ACTUALIZACIÓN" que se realizan en las columnas exactas que estamos leyendo de la base de datos. Parece que Hibernate está escribiendo automáticamente estos registros en la base de datos (aunque no lo solicitemos). Eso probablemente explicaría por qué hay un punto muerto.

¿Podría ser por una Sesión FLUSH o algo similar? Pareciendo más como la solución podría ser usar un envoltorio de transacción con readOnly en él ...

Respuesta

1

Finalmente determinamos que la solución era envolverlo en una transacción de solo lectura.

No tengo claro por qué, no estábamos usando los setters (simplemente leyendo los datos) - nada estaba cambiando en la base de datos. Pero, por alguna razón, Hibernate intentaba volver a escribir la misma información y causaba un bloqueo cuando otro proceso intentaba leer esos registros.

¡El uso de la transacción readOnly hizo que el problema desapareciera!

<bean id="txWrappedOrderDataService" 
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 
<property name="transactionManager" ref="transactionManager"/> 
<property name="target" ref="orderDataService"/> 
<property name="transactionAttributes"> 
    <props> 
     <!-- all methods require a transaction --> 
     <prop key="*">PROPAGATION_REQUIRED,readOnly</prop> 
    </props> 
</property> 

0

¿Ha comprobado si hay factores desencadenantes en la base de datos? ¿Estás seguro de que es Hibernate y no otro proceso que actualiza esas mismas filas? Tal vez hay una columna que almacena una marca de tiempo de la última lectura y se actualiza cada vez que se lee la fila (aunque no puedo recordar que podría hacer SELECT desencadenantes) ...

2

Involuntary pueden ocurrir actualizaciones con hibernación cuando usa setters que manipulan el valor que realmente establecen. Un ejemplo sería un setter para un atributo de cadena que reemplaza un valor de nulo con "". Un posible candidato también son colecciones. Asegúrese de que los setters no reemplacen la colección contenida. Si reemplaza una colección de una entidad con otra colección que contenga los mismos contenidos, hibernate no podrá darse cuenta y actualizar la colección completa.

+0

Esto no es exactamente lo que estaba sucediendo, pero parecía similar. Los valores no se estaban "configurando" en el código. De hecho, Hibernate intentaba volver a escribir los mismos datos por algún motivo (los datos en el DB nunca se actualizaban con valores diferentes). – jonathanq

0

Jens es correcto

Para añadir situ que necesita para inspeccionar de cerca sus setters y getters y ver si devuelven un valor diferente en diferentes llamadas por ejemplo, new Date() - esto se obtendrá una nueva de valor cada una tiempo se llama y hará que hibernate piense que el objeto ha cambiado

0

Una cosa interesante que hacer es añadir

log4j.logger.org.hibernate.persister.entity.AbstractEntityPersister = TRACE

en la configuración de log4j. Al hacer eso, hibernate registrará por qué una entidad necesita una actualización en la base de datos. Tuvimos algunos problemas con las entidades que devolvían "" cuando una propiedad era nula y causaba actualizaciones en la base de datos. Al hacer eso, pudimos identificar tales problemas.

0

He visto este problema suceder en nuestro sistema cuando nos faltan índices. Las consultas que se están ejecutando en la base de datos se están ejecutando demasiado debido a que faltan índices en las columnas clave, lo que bloquea la tabla.

Cuestiones relacionadas