2012-02-02 21 views
7

如果同一代码的多个实例在不同的服务器上运行,我想使用数据库来确保一个服务器上的进程无法在另一台服务器上运行。如何使用数据库来管理信号量?

我可能会想出一些使用Oracle事务处理,锁存器或其他任何可行的SQL命令,但我宁愿找到一些已经尝试过并且真实的事情。

几年前,一名SQL开发人员拥有一个单一的SQL事务处理信号量,如果它得到它就返回true,如果它没有得到它,则返回false。然后在我的处理结束时,我需要运行另一个SQL事务来释放信号量。这很酷,但我不知道数据库支持的信号是否有可能超时。这将是一个巨大的奖金,暂停!

编辑:

这里有什么可能是一些可行的SQL命令,但除非通过cron作业黑客没有超时:

--------------------------------------------------------------------- 
--Setup 
--------------------------------------------------------------------- 
CREATE TABLE "JOB_LOCKER" ("JOB_NAME" VARCHAR2(128 BYTE), "LOCKED" VARCHAR2(1 BYTE), "UPDATE_TIME" TIMESTAMP (6)); 
CREATE UNIQUE INDEX "JOB_LOCKER_PK" ON "JOB_LOCKER" ("JOB_NAME") ; 
ALTER TABLE "JOB_LOCKER" ADD CONSTRAINT "JOB_LOCKER_PK" PRIMARY KEY ("JOB_NAME"); 
ALTER TABLE "JOB_LOCKER" MODIFY ("JOB_NAME" NOT NULL ENABLE); 
ALTER TABLE "JOB_LOCKER" MODIFY ("LOCKED" NOT NULL ENABLE); 

insert into job_locker (job_name, locked) values ('myjob','N'); 
commit; 

--------------------------------------------------------------------- 
--Execute at the beginning of the job 
--AUTOCOMMIT MUST BE OFF! 
--------------------------------------------------------------------- 
select * from job_locker where job_name='myjob' and locked = 'N' for update NOWAIT; 
--returns one record if it's ok. Otherwise returns ORA-00054. Any other thread attempting to get the record gets ORA-00054. 
update job_locker set locked = 'Y', update_time = sysdate where job_name = 'myjob'; 
--1 rows updated. Any other thread attempting to get the record gets ORA-00054. 
commit; 
--Any other thread attempting to get the record with locked = 'N' gets zero results. 
--You could have code to pull for that job name and locked = 'Y' and if still zero results, add the record. 

--------------------------------------------------------------------- 
--Execute at the end of the job 
--------------------------------------------------------------------- 
update job_locker set locked = 'N', update_time = sysdate where job_name = 'myjob'; 
--Any other thread attempting to get the record with locked = 'N' gets no results. 
commit; 
--One record returned to any other thread attempting to get the record with locked = 'N'. 

--------------------------------------------------------------------- 
--If the above 'end of the job' fails to run (system crash, etc) 
--The 'locked' entry would need to be changed from 'Y' to 'N' manually 
--You could have a periodic job to look for old timestamps and locked='Y' 
--to clear those. 
--------------------------------------------------------------------- 
+2

为避免手动过期记录,可以将锁定为时间戳。获取锁可将时间戳设置为现在。要获取锁,对锁的查询需要添加一个条件WHERE locktime <(SYSDATE - timeToExpire)。然后锁自动过期。 – Glenn 2012-02-02 18:39:51

+0

感谢Glenn ...时间戳而不是Y/N是上面的一个很好的补充。 – Dale 2012-02-08 16:29:23

回答

11

你应该看看DBMS_LOCK。实质上,它允许Oracle在内部使用排队锁定机制,除了它允许您定义锁定类型'UL'(用户锁定)。锁可以保持共享或排他性,并且锁取锁或将锁从一种模式转换为另一种模式的请求支持超时。

我认为它会做你想做的。

希望有所帮助。

+0

我喜欢这有一个超时。我以前从来没有使用过DBMS_LOCK,而且我也不是一个真正的SQL骑师,所以对我来说这可能是一场艰苦的战斗,嘿嘿。 – Dale 2012-02-02 17:44:48