【发布时间】:2015-07-09 22:00:12
【问题描述】:
我正在尝试为在不同 JBoss 节点中运行的 cron 作业实现某种信号量。我正在尝试使用数据库(Oracle 11g)作为锁定机制,使用一个表来同步不同节点中的 cron 作业。 表格很简单:
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 的条目,并检查它是否已经在运行。如果不是,它将条目设置运行标志更新为真。第一个选择是通过 JPA CriteriaApi 使用 Hibernate 和悲观锁进行的。
query.setLockMode(javax.persistence.LockModeType.PESSIMISTIC_WRITE);
所有这些操作都在一个事务中完成。
当一个进程运行时,它发出的查询如下:
[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) 上进行此选择以进行更新,并且该工具在此工具中运行良好。但是,如果我在 SQL 工具上选择更新并启动 cron 作业,它们不会被阻塞并且运行时不会出现问题。
我认为问题来自 JPA、Hibernate 或 Oracle 驱动程序,但我不确定。关于问题出在哪里的任何想法?我应该使用 anotehr 策略吗? 提前致谢。
【问题讨论】:
-
你是否在 JBOSS 中实现了“主动”等待?在您将连接返回到连接池的那一刻,JBOSS 将对其发出 ROLLBACK 并且所有锁都将丢失。此外,您的 cron 作业必须使用 SELECT FOR UPDATE,Oracle 中永远不会阻止纯读取。
-
我不确定您所说的主动等待是什么意思,我不使用任何 JBOSS 特殊功能。我希望在数据库选择操作中实现等待功能。
-
我认为这与悲观锁定无关,而与一般事务有关。只要事务是打开的并且连接保持在线程中,即使是纯单一的 UPDATE 也可以工作。在 JPA 中,当代码离开事务边界时,会发出回滚,并且所有锁都会丢失。
-
Ibre,我面临的问题是 cron 作业启动事务,如果数据显示其他 cron 作业正在运行,则使用“for update”读取正确的行,它只会关闭事务否则它将运行的行设置更新为 true 并关闭事务。我认为这种方法很好,但也许它不是实现我所寻找的正确方法。
标签: java oracle hibernate jpa pessimistic-locking