2015-04-30 99 views
10

我試圖爲在不同的JBoss節點中運行的cron作業實現某種信號量。我試圖使用數據庫(Oracle 11g)作爲鎖定機制,使用一個表來同步不同節點中的cron作業。 表是非常簡單的:爲什麼我的悲觀鎖定JPA與Oracle不起作用

CREATE TABLE SYNCHRONIZED_CRON_JOB_TASK 
(
    ID   NUMBER(10)   NOT NULL, 
    CRONJOBTYPE VARCHAR2(255 Byte), 
    CREATIONDATE TIMESTAMP(6)   NOT NULL, 
    RUNNING  NUMBER(1) 
); 

ALTER TABLE SYNCHRONIZED_CRON_JOB_TASK 
    ADD CONSTRAINT PK_SYNCHRONIZED_CRON_JOB_TASK 
    PRIMARY KEY (ID); 

因此,當一個作業開始它在表上搜索其cronjobtype的進入,並檢查它是否已經在運行。如果不是,則將條目設置運行標誌更新爲true。第一個選擇是使用Hibernate和Pessimistic Lock使用JPA CriteriaApi進行的。

query.setLockMode(javax.persistence.LockModeType.PESSIMISTIC_WRITE); 

所有這些操作都是在一個事務中完成的。

當一個進程中運行,它使querys如下:

[Server:server-two] 10:38:00,049 INFO [stdout] (scheduler-2) 2015-04-30 10:38:00,048 WARN (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes 
[Server:server-two] 10:38:00,049 INFO [stdout] (scheduler-2) Hibernate: select * from (select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=?) where rownum <= ? 
[Server:server-two] 10:38:00,053 INFO [stdout] (scheduler-2) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update 
[Server:server-two] 10:38:00,056 INFO [stdout] (scheduler-2) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=? 

沒有與此警告沒有問題,可以看到第一選擇,然後選擇要更新,所以Oracle應該阻止其他選擇該行的操作。 但這就是要點,查詢沒有被阻止,所以兩個作業可以進入並使選擇和更新沒有問題。鎖定不工作,我們可以看到,如果我們同時運行兩個cron作業:

[Server:server-one] 10:38:00,008 INFO [stdout] (scheduler-3) 2015-04-30 10:38:00,008 WARN (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes 
[Server:server-two] 10:38:00,008 INFO [stdout] (scheduler-2) 2015-04-30 10:38:00,008 WARN (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes 
[Server:server-two] 10:38:00,009 INFO [stdout] (scheduler-2) Hibernate: select * from (select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=?) where rownum <= ? 
[Server:server-one] 10:38:00,009 INFO [stdout] (scheduler-3) Hibernate: select * from (select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=?) where rownum <= ? 
[Server:server-two] 10:38:00,013 INFO [stdout] (scheduler-2) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update 
[Server:server-one] 10:38:00,014 INFO [stdout] (scheduler-3) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update 
[Server:server-two] 10:38:00,016 INFO [stdout] (scheduler-2) 2015-04-30 10:38:00,015 DEBUG (SynchronizedCronJobService.java:65) - Task read SynchronizedCronJobTask [id=185, type=AlertMailTaskExecutor, creationDate=2015-04-25 07:11:33.0, running=false] 
[Server:server-two] 10:38:00,018 INFO [stdout] (scheduler-2) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=? 
[Server:server-one] 10:38:00,022 INFO [stdout] (scheduler-3) 2015-04-30 10:38:00,022 DEBUG (SynchronizedCronJobService.java:65) - Task read SynchronizedCronJobTask [id=185, type=AlertMailTaskExecutor, creationDate=2015-04-25 07:11:33.0, running=false] 
[Server:server-one] 10:38:00,024 INFO [stdout] (scheduler-3) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=? 

我試圖讓這個選擇爲上的SQL工具(SQLWorkbenchJ)帶兩個連接更新和bloking工作在這個工具內罰款。但是,如果我選擇SQL工具上的更新並啓動cron作業,則它們不會混亂並且沒有問題地運行。

我認爲問題來自JPA,Hibernate或Oracle驅動程序,但我不確定。任何想法在哪裏是問題?我應該使用anotehr策略嗎? 在此先感謝。

+1

你會在JBOSS內部實現「主動」嗎?在你將連接返回到連接池的時候,JBOSS會對它發出ROLLBACK,並且所有的鎖都會丟失。您的cron-jobs也必須使用SELECT FOR UPDATE,純粹的閱讀在Oracle中永遠不會被阻止。 – ibre5041

+0

我不確定你的主動等待是什麼意思,我不使用任何JBOSS特殊功能。我希望等待的功能是在數據庫選擇操作上完成的。 –

+0

我認爲這不是關於悲觀鎖定,而是關於一般交易。只要事務處於打開狀態並且連接保留在線程中,即使是純粹的單個UPDATE也可以工作。在JPA中,當代碼離開事務邊界回滾並且所有鎖都丟失時。 – ibre5041

回答

4

最後我設法使它工作,但有一些修飾。這個想法是使用LockModeType.PESSIMISTIC_FORCE_INCREMENT而不是PESSIMISTIC_WRITE。使用此鎖定模式,Cron Jobs的行爲如下:

  1. 當第一個作業使select for update一切正常,但對象上的版本發生變化時。
  2. 如果另一個作業試圖在第一個作業仍在其事務中時進行同樣的選擇,則JPA將啓動OptimisticLockException,因此如果捕獲該異常,則可以確保它是針對讀取鎖進行引發的。

該解決方案具有不同的同行:

  1. SynchronizedCronJobTask必須有一個版本字段,並在版本控制下與@Version
  2. 您需要處理OptimisticLockException,它應該是事務性服務外抓方法以便在發生解鎖時進行回滾。
  3. 恕我直言是一個非常優雅的解決方案,比Cron Jobs等待以前的Jobs完成的鎖簡單得多。
2

將鎖定模式設置爲PESSIMISTIC_READ,因爲您需要第二臺服務器在更改數據之前知道第一臺服務器的更改。

+0

我試過PESSIMISTIC_READ,但它不起作用。另外,對於這種情況,右鎖定模式是PESSIMISTIC_WRITE,因爲它可以阻止讀取和更新。通過PESSIMISTIC_READ,只有更新被阻止。 –

+0

你有一個錯誤。反之亦然:PESSIMISTIC_READ鎖讀取和寫入,但PESSIMISTIC_WRITE - 只寫入。 –

+0

嗨亞歷山大。你確定?在這個線程http://stackoverflow.com/questions/1657124/whats-the-difference-between-pessimistic-read-and-pessimistic-write約瑟夫的回答說: - PESSIMISTIC_READ。 ...此鎖定模式不會阻止其他事務讀取數據。不過,我試圖使用PESSIMISTIC_READ,但沒有運氣。也許我錯過了一些東西。 –