簡體   English   中英

Java / EJB / Hibernate事務同步以防止Oracle死鎖

[英]Java/EJB/Hibernate Transaction Synchronization to Prevent Oracle Deadlocks

我有一個每5分鍾運行一次的Quartz作業,我在application.log和Oracle跟蹤日志中看到了死鎖錯誤。 我正在尋求解決這些死鎖錯誤,但我不確定它們是否由於鎖定策略,事務隔離或Quartz並發相關問題而發生。 請讓我知道我的鎖定策略可能有什么問題,我可能會遺漏什么,或者我應該采取什么步驟來解決這個問題。

我正在創建TypedQuery,它會像這樣鎖定行:

TypedQuery<Order> ordersQuery = em.createQuery("SELECT o from Order o where o.orderDate <= :today and o.statusId = :statusId", Order.class);

並使用以下命令設置LockModeType:

ordersQuery.setLockMode(LockModeType.PESSIMISTIC_WRITE);

Quartz作業在無狀態會話bean中調用以下方法。 它首先獲取orderIds進行處理(這是“for update”鎖定發生的地方),然后使用那些orderIds調用initiateOrders(List)。 在類或這些方法上沒有指定TransactionAttributeTypes,因此它應該默認為TransactionAttributeType.REQUIRED。

public int initiateOrders(List<Integer> orderIds) throws Exception {
    if (orderIds.size() == 0) {
      return 0;
    }

    List<Order> orders = orderDao.findByIds(orderIds);

    for (Order o : orders) {
      o.setProcessId(-1);
      boolean isSuccess;

      if (o.getOrderSummaryRequiredBy() != null && o.getOrderSummaryRequiredBy().equals("ACC")) {
        o.setRouteToDept(o.getRouteToEntity());
      }

      isSuccess = orderManager.startOrderProcess(o);
      invokeUpdateOrderTemplateActiveYN(o.getOrderId());
      if (!isSuccess) {
        log.error("Exception in startOrderProcess( " + o.getOrderId() + " )");
      }
    }

    return orders.size();
}

public List<Integer> getOrderIdsToProcess(int packetSize) {
    TypedQuery<Order> ordersQuery = entityManager
        .createQuery(
            "SELECT o from Order o where o.orderDate <= :today and o.statusId = :statusId and o.processId = :processId",
            Order.class);

    java.sql.Date today = new java.sql.Date(new Date().getTime());

    ordersQuery.setParameter("today", today);
    ordersQuery.setParameter("statusId", 1);
    ordersQuery.setParameter("processId", 0);
    ordersQuery.setMaxResults(packetSize);

    ordersQuery.setLockMode(LockModeType.PESSIMISTIC_WRITE);

    List<Order> orders = ordersQuery.getResultList();
    List<Integer> orderIds = new ArrayList<>();

    for (Order o : orders) {
      o.setProcessId(-1);
      orderIds.add(o.getOrderId());
    }

    return orderIds;
}

startOrderProcess(Order)也位於具有默認事務屬性類型的無狀態bean中。 它在訂單對象上設置一些值並調用entityManager.merge(order);

invokeUpdateOrderTemplateActiveYN(Integer)調用存儲過程,該存儲過程從Order中選擇並更新不同的表。

應用程序日志(稍微減少並刪除PII):

15:31:42,969 DEBUG [org.hibernate.SQL] (myScheduler_Worker-10) select MY_SEQ.nextval from dual
15:31:42,979 DEBUG [org.hibernate.SQL] (myScheduler_Worker-10) select assignedus0_.ORDER_ID as ORDER_2_90_1_, from ASSIGNED_USERS assignedus0_ where assignedus0_.ORDER_ID=?
15:31:42,979 DEBUG [org.hibernate.SQL] (myScheduler_Worker-10) insert into ORDER_EVENT_LOG (ACTION, ... EVENT_LOG_ID) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
15:31:42,979 DEBUG [org.hibernate.SQL] (myScheduler_Worker-10) update ORDER set ... where ORDER_ID=?
15:31:47,513 DEBUG [org.hibernate.SQL] (myScheduler_Worker-5) select user0_.USER_ID as USER_ID1_116_0_, user0_.ACTIVE_YN as ACTIVE_Y2_116_0_, ... from USER user0_ where user0_.USER_ID=?
15:31:47,533 DEBUG [org.hibernate.SQL] (myScheduler_Worker-5) select orderinf0_.ORDER_INFORMATION_ID as ORDER_1_88_0_, ... from ORDER_INFORMATION orderinf0_ where orderinf0_.ORDER_INFORMATION_ID=?
15:31:47,673 DEBUG [org.hibernate.SQL] (myScheduler_Worker-5) select orderinf0_.ORDER_INFO_CATEGORY_ID as ORDER_1_89_0_, ... from ORDER_INFORMATION_CATEGORY orderinf0_ where orderinf0_.ORDER_INFO_CATEGORY_ID=?
15:31:47,673 DEBUG [org.hibernate.SQL] (myScheduler_Worker-5) select user0_.USER_ID as USER_ID1_116_0_, user0_.ACTIVE_YN as ACTIVE_Y2_116_0_,... where user0_.USER_ID=?
15:31:48,151 WARN  [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (myScheduler_Worker-2) SQL Error: 60, SQLState: 61000
15:31:48,151 ERROR [org.hibernate.engine.jdbc.spi.SqlExceptionHelper] (myScheduler_Worker-2) ORA-00060: deadlock detected while waiting for resource

15:31:48,181 DEBUG [org.hibernate.SQL] (myScheduler_Worker-10) select orderdis0_.ORDER_ID as ORDER_4_90_1_, ... from ORDER_DISPATCH orderdis0_ where orderdis0_.ORDER_ID=?
15:31:48,181 ERROR [org.jboss.as.ejb3.invocation] (myScheduler_Worker-2) JBAS014134: EJB Invocation failed on component OrderManager for method public java.util.List com.business.model.order.OrderManager.getOrdersToProcess(int): javax.ejb.EJBException: javax.persistence.PessimisticLockException: could not obtain pessimistic lock
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.handleExceptionInOurTx(CMTTxInterceptor.java:191) [jboss-as-ejb3-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    ...
    at org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:73) [jboss-as-ee-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at com.business.model.order.OrderManager$$$view26.getOrdersToProcess(Unknown Source) [model-1.0.jar:]
    at com.business.workflow.jobs.OrderProcessor.execute(OrderProcessor.java:59) [workflow-jobs-1.0.jar:]
    at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [quartz-1.6.0.jar:1.6.0]
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:529) [quartz-1.6.0.jar:1.6.0]
Caused by: javax.persistence.PessimisticLockException: could not obtain pessimistic lock
    at org.hibernate.ejb.AbstractEntityManagerImpl.wrapLockException(AbstractEntityManagerImpl.java:1449) [hibernate-entitymanager-4.2.18.Final-redhat-2.jar:4.2.18.Final-redhat-2]
    ...
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:277) [hibernate-entitymanager-4.2.18.Final-redhat-2.jar:4.2.18.Final-redhat-2]
    at com.business.model.order.OrderManager.getOrdersToProcess(OrderManager.java:2655) [model-1.0.jar:]
    ... 27 more
Caused by: org.hibernate.dialect.lock.PessimisticEntityLockException: could not obtain pessimistic lock
    at org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy.lock(PessimisticWriteSelectLockingStrategy.java:114) [hibernate-core-4.2.18.Final-redhat-2.jar:4.2.18.Final-redhat-2]
    ... 59 more
Caused by: org.hibernate.exception.LockAcquisitionException: could not extract ResultSet
    ...
    at org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy.lock(PessimisticWriteSelectLockingStrategy.java:87) [hibernate-core-4.2.18.Final-redhat-2.jar:4.2.18.Final-redhat-2]
    ... 81 more
Caused by: java.sql.SQLException: ORA-00060: deadlock detected while waiting for resource
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:450) [ojdbc-7.jar:12.1.0.1.0]
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:399) [ojdbc-7.jar:12.1.0.1.0]
    ...
    at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeQuery(WrappedPreparedStatement.java:462)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:79) [hibernate-core-4.2.18.Final-redhat-2.jar:4.2.18.Final-redhat-2]
    ... 82 more

15:31:48,191 INFO  [org.quartz.core.JobRunShell] (myScheduler_Worker-2) Job workflow.OrderProcessor threw a JobExecutionException: : org.quartz.JobExecutionException: javax.ejb.EJBException: javax.persistence.PessimisticLockException: could not obtain pessimistic lock [See nested exception: javax.ejb.EJBException: javax.persistence.PessimisticLockException: could not obtain pessimistic lock]
    at com.business.workflow.jobs.OrderProcessor.execute(OrderProcessor.java:66) [workflow-jobs-1.0.jar:]
    at org.quartz.core.JobRunShell.run(JobRunShell.java:202) [quartz-1.6.0.jar:1.6.0]
    at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:529) [quartz-1.6.0.jar:1.6.0]
Caused by: javax.ejb.EJBException: javax.persistence.PessimisticLockException: could not obtain pessimistic lock
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.handleExceptionInOurTx(CMTTxInterceptor.java:191) [jboss-as-ejb3-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    ...
    at org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:73) [jboss-as-ee-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    at com.business.model.order.OrderManager$$$view26.getOrdersToProcess(Unknown Source) [model-1.0.jar:]
    at com.business.workflow.jobs.OrderProcessor.execute(OrderProcessor.java:59) [workflow-jobs-1.0.jar:]
    ... 2 more
Caused by: javax.persistence.PessimisticLockException: could not obtain pessimistic lock
    at org.hibernate.ejb.AbstractEntityManagerImpl.wrapLockException(AbstractEntityManagerImpl.java:1449) [hibernate-entitymanager-4.2.18.Final-redhat-2.jar:4.2.18.Final-redhat-2]
    ...
    at com.business.model.order.OrderManager.getOrdersToProcess(OrderManager.java:2655) [model-1.0.jar:]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.7.0_79]
    ...
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:280) [jboss-as-ejb3-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    ... 27 more
Caused by: org.hibernate.dialect.lock.PessimisticEntityLockException: could not obtain pessimistic lock
    at org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy.lock(PessimisticWriteSelectLockingStrategy.java:114) [hibernate-core-4.2.18.Final-redhat-2.jar:4.2.18.Final-redhat-2]
    ...
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:268) [hibernate-entitymanager-4.2.18.Final-redhat-2.jar:4.2.18.Final-redhat-2]
    ... 59 more
Caused by: org.hibernate.exception.LockAcquisitionException: could not extract ResultSet
    at org.hibernate.dialect.Oracle8iDialect$2.convert(Oracle8iDialect.java:450) [hibernate-core-4.2.18.Final-redhat-2.jar:4.2.18.Final-redhat-2]
    ...
    at org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy.lock(PessimisticWriteSelectLockingStrategy.java:87) [hibernate-core-4.2.18.Final-redhat-2.jar:4.2.18.Final-redhat-2]
    ... 81 more
Caused by: java.sql.SQLException: ORA-00060: deadlock detected while waiting for resource

    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:450) [ojdbc-7.jar:12.1.0.1.0]
    ...
    at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeQuery(WrappedPreparedStatement.java:462)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:79) [hibernate-core-4.2.18.Final-redhat-2.jar:4.2.18.Final-redhat-2]
    ... 82 more

Oracle跟蹤日志:

    =====================
PARSING IN CURSOR #4 len=87 dep=0 uid=29 oct=3 lid=29 tim=1416963939942628 hv=3017806794 ad='48e71488'
select ORDER_ID from ORDER where ORDER_ID =:1  for update
END OF STMT
PARSE #4:c=0,e=7,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1416963939942625
EXEC #4:c=0,e=43,p=0,cr=3,cu=1,mis=0,r=0,dep=0,og=4,tim=1416963939942708
FETCH #4:c=0,e=9,p=0,cr=0,cu=0,mis=0,r=1,dep=0,og=4,tim=1416963939942747
STAT #4 id=1 cnt=1 pid=0 pos=1 obj=0 op='FOR UPDATE  (cr=3 pr=0 pw=0 time=35 us)'
STAT #4 id=2 cnt=1 pid=1 pos=1 obj=82422 op='INDEX UNIQUE SCAN PK_ORDER (cr=3 pr=0 pw=0 time=12 us)'
=====================
PARSING IN CURSOR #3 len=87 dep=0 uid=29 oct=3 lid=29 tim=1416963939943362 hv=3017806794 ad='48e71488'
select ORDER_ID from ORDER where ORDER_ID =:1  for update
END OF STMT
PARSE #3:c=0,e=8,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=1416963939943359
*** 2015-12-24 15:31:45.629
DEADLOCK DETECTED ( ORA-00060 )
[Transaction Deadlock]
The following deadlock is not an ORACLE error. It is a
deadlock due to user error in the design of an application
or from issuing incorrect ad-hoc SQL. The following
information may aid in determining the deadlock:
Deadlock graph:
                       ---------Blocker(s)--------  ---------Waiter(s)---------
Resource Name          process session holds waits  process session holds waits
TX-00160021-002790fd        47    1799     X             51    1856           X
TX-00190009-0018cd94        51    1856     X             47    1799           X
session 1799: DID 0001-002F-0000004F    session 1856: DID 0001-0033-00000FCE
session 1856: DID 0001-0033-00000FCE    session 1799: DID 0001-002F-0000004F
Rows waited on:
Session 1856: obj - rowid = 000141F4 - AAAUH0ABVAAAdiXAAT
  (dictionary objn - 82420, file - 85, block - 120983, slot - 19)
Session 1799: obj - rowid = 000141F4 - AAAUH0ABVAAAdjJAAT
  (dictionary objn - 82420, file - 85, block - 121033, slot - 19)
Information on the OTHER waiting sessions:
Session 1856:
  pid=51 serial=19150 audsid=69033382 user: 29/ORAUSER
  O/S info: user: xxxx, term: unknown, ospid: 1234, machine: XXXX
            program: JDBC Thin Client
  application name: JDBC Thin Client, hash value=0
  Current SQL Statement:

update ORDER set ... where ORDER_ID=:75 
End of information on OTHER waiting sessions.
Current SQL statement for this session:
select ORDER_ID from ORDER where ORDER_ID =:1  for update
===================================================
PROCESS STATE
-------------
Process global information:
     process: 0x15e4ac4d0, call: 0x141bfe118, xact: 0x15b8275d0, curses: 0x15d202070, usrses: 0x15d202070
  ----------------------------------------
  SO: 0x15e4ac4d0, type: 2, owner: (nil), flag: INIT/-/-/0x00
  (process) Oracle pid=47, calls cur/top: 0x141bfe118/0x141bfe118, flag: (0) -
            int error: 0, call error: 0, sess error: 0, txn error 0
  (post info) last post received: 0 0 193
              last post received-location: kgskbwt: posting new actses to run
              last process to post me: 15e4a9d48 167 0
              last post sent: 0 0 193
              last post sent-location: kgskbwt: posting new actses to run
              last process posted by me: 15e4a8d78 202 0
    (latch info) wait_event=0 bits=0
    Process Group: DEFAULT, pseudo proc: 0x15e812e98
    O/S info: user: oracle, term: UNKNOWN, ospid: 12914
    OSD pid info: Unix process pid: 12914, image: oracleprod@pddevdb01
Dump of memory from 0x000000015E3B6870 to 0x000000015E3B6A78

環境: JBoss EAP 6,Oracle 10g,Hibernate 4.2,Quartz 1.6和EJB 3。

注意:升級Oracle目前不是一種選擇。 DB和應用程序事務隔離級別都沒有更改。


更新:使用兩台機器進行調試后,我們可以從“select ... for update”查詢中檢索相同的結果。 給定事務A運行for update查詢並且事務B 事務A提交之前運行它以進行更新查詢,它將在返回結果之前等待A提交,但是一旦A提交,B返回與A相同的結果 - 即使值為選擇on(processId)已經被A(改為-1)改變了。 如果A在B運行之前提交它的“for update”查詢,則兩個查詢會得到不同的結果。

可以在此日志中顯示該問題:

update ORDER set ... where ORDER_ID=:75 
End of information on OTHER waiting sessions.
Current SQL statement for this session:
select ORDER_ID from ORDER where ORDER_ID =:1  for update

您正在獲取鎖定但僅在Quartz事務結束時刷新並提交狀態,這可能需要一些時間。

同時,所有這些記錄都被鎖定,沒有其他事務可以選擇或更新這些記錄。

我認為您應該按如下方式更改執行順序:

  • 保留select for update邏輯以鎖定要處理的條目並將狀態更新為IN_PROGRESS
  • 您盡快提交該事務以釋放鎖
  • 繼續在新事務中現在為IN_PROGRESS的所選記錄
  • 另一個事務不會選擇IN_PROGRESS記錄,這樣就不會有兩個並發事務處理相同的記錄

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM