Oracle Deadlock quando l'applicazione Hibernate carica i dati per un utilizzo di sola lettura

StackOverflow https://stackoverflow.com/questions/663250

Domanda

Stiamo riscontrando un errore Oracle Deadlock (org.hibernate.util.JDBCExceptionReporter - ORA-00060: deadlock rilevato durante l'attesa della risorsa). È stato suggerito che il problema riguarda un processo che esegue operazioni di sola lettura utilizzando Hibernate mentre un altro processo sta eseguendo un aggiornamento sulla stessa riga.

Il processo di sola lettura in questione è configurato utilizzando Hibernate e Spring. Non abbiamo definito esplicitamente una transazione per il servizio. Anche se potrebbe non essere l'ideale - non riesco a capire perché Hibernate avrebbe provato a ottenere un blocco esclusivo su una riga quando non sono state eseguite operazioni di salvataggio / aggiornamento - solo un get / load.

Quindi la mia domanda è: Hibernate, quando non è definita alcuna gestione esplicita delle transazioni, cerca di ottenere un blocco di lettura / scrittura su una riga anche se solo un " load " di un oggetto viene eseguito. Non viene eseguito alcun salvataggio / aggiornamento.

È possibile che la definizione di una transazione attorno al servizio che sta caricando i dati e quindi la specifica READONLY sulla transazioneAttributes causi a Hibernate di ignorare un blocco di righe già esistente e caricare i dati per scopi di sola lettura?

Ecco alcuni esempi di codice:

Per caricare il record stiamo usando un HibernateDaoTemplate

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

La configurazione di Spring per il servizio che chiama questo metodo è:

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

Il deadlock effettivo si sta verificando su uno dei record PurchaseOrderItem caricati dalla chiamata al caricamento di OrderOrder.

Ciò provocherebbe un deadlock se il record da caricare fosse bloccato da un altro processo? E se fosse così, l'aggiunta di un wrapper di transazione come quello qui sotto risolverebbe il problema?

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

Aggiornamento: Il team di DataBase ha visto sul server messaggi di traccia che sembrano indicare che il nostro " solo " il processo sta effettivamente scrivendo automaticamente nel database. Sono registrati & Quot; UPDATE & Quot; comandi eseguiti sulle colonne esatte che stiamo leggendo dal database. Sembra che Hibernate scriva automaticamente questi record nel database (anche se non lo stiamo chiedendo). Ciò spiegherebbe probabilmente perché c'è un deadlock.

Potrebbe essere dovuto a un FLUSH di sessione o qualcosa di simile? La soluzione potrebbe essere quella di utilizzare un wrapper di transazione con readOnly su di esso ...

È stato utile?

Soluzione 2

Abbiamo finalmente stabilito che la soluzione era di racchiuderlo in una transazione readOnly.

Non sono chiaro il perché, non stavamo usando i setter (semplicemente leggendo i dati) - non è stato cambiato nulla nel database. Ma per qualche ragione Hibernate stava cercando di riscrivere gli stessi dati e stava causando un blocco quando un altro processo ha provato a leggere quei record.

L'uso della transazione readOnly ha causato la scomparsa del problema!

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

Altri suggerimenti

Potrebbero verificarsi aggiornamenti involontari con ibernazione quando si utilizzano setter che modificano il valore effettivamente impostato. Un esempio potrebbe essere un setter per un attributo String che sostituisce un valore null con & Quot; & Quot ;. Un probabile candidato sono anche raccolte. Assicurarsi che i setter non sostituiscano la raccolta contenuta. Se sostituisci una raccolta di un'entità con un'altra raccolta contenente gli stessi contenuti, l'ibernazione non sarà in grado di realizzarlo e aggiornerà la raccolta completa.

Hai verificato la presenza di trigger nel database? Sei sicuro che sia Hibernate e non qualche altro processo che aggiorna quelle stesse righe? Forse c'è una colonna che memorizza un timestamp dell'ultima lettura e viene aggiornata ogni volta che la riga viene letta (anche se non ricordo dalla parte superiore della mia testa che potresti fare trigger SELECT) ...

Jens ha ragione

Per aggiungere, è necessario ispezionare attentamente i setter e i getter e vedere se restituiscono un valore diverso su chiamate diverse es. new Date () - questo restituirebbe un nuovo valore - ogni volta che viene chiamato e farà ibernare pensare che l'oggetto sia cambiato

Una cosa interessante da fare è aggiungere

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

nella configurazione di log4j. In questo modo l'ibernazione registrerà perché un'entità deve essere aggiornata nel database. Abbiamo riscontrato alcuni problemi con le entità che restituivano & Quot; & Quot; quando una proprietà era nulla causando aggiornamenti nel DB. In questo modo siamo stati in grado di individuare tali problemi.

Ho visto questo problema accadere nel nostro sistema quando avevamo degli indici mancanti. Le query eseguite nel database sono in esecuzione troppo a lungo a causa della mancanza di indici nelle colonne chiave, bloccando così la tabella.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top