我正在使用PHP开发一个Web项目,用户点击" Reload"按钮,一个长时间运行的过程开始。
我想要发生的是,如果一个用户已经在运行此流程,那么其他用户可以告诉他们何时加载页面,流程已经在运行,什么时候开始,以及谁启动了。它们不应该在它已经运行时再次启动它。
我可以做这样的事情(简化下面的SQL):
-- Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
SELECT 1 FROM institutions WHERE institution_code = '15' FOR UPDATE;
UPDATE institutions SET process_start = CURRENT_TIMESTAMP,
process_acct_id = 101, process_finish = null WHERE institution_code = '15';
-- Process Runs here, taking a couple of hours.
UPDATE institutions SET process_finish = CURRENT_TIMESTAMP
WHERE institution_code = '15';
COMMIT;
上述问题是其他线程无法在进程运行且事务已提交之前看到process_acct_id,process_start和process_finish。我怎么写它以便它们可以?我是否在自治事务中运行第一个更新?或者有更好的方法吗?
答案 0 :(得分:0)
我不熟悉Oracle,所以语法不是正确的,但我会在事务中只进行一次操作。我使用了“Active”列而不是时间戳来简化它,但您可以通过使用时间戳来计算出类似的逻辑。此外,我假设你通过insititution代码确定行动。
BEGIN TRAN
UPDATE Active = 1 WHERE Active = 0 AND insititution_code = @InstitutionCode
IF @@ROWCOUNT = 0
BEGIN
RETURN 0
END
ELSE
BEGIN
RETURN 1
END
COMMIT
这里的要点是Update将阻止其他事务,如果已设法更新行,则代码块将返回1,否则返回0。然后,您可以添加逻辑,知道成功将标志设置为1的那个是唯一正在运行的逻辑。
这种方法的缺点是,如果您忘记或未能将标志设置为0,则其他进程将无法为该活动运行。
答案 1 :(得分:0)
您需要在第一次更新后提交,但是您还需要更新以检查进程ID是否已经设置 - 因此第二个调用者将在此时停止。如果您可以使用PHP层发出的几个语句来执行此操作,那么大纲将是:
UPDATE institutions
SET process_acct_id = 101,
process_start = CURRENT_TIMESTAMP,
process_finish = null
WHERE institution_code = '15'
AND process_acct_id IS NULL; -- so no rows will be updated if a process is running
-- In PHP layer, check how may rows were updated (like SQL%ROWCOUNT)
-- If zero then another process is already running, so stop
-- With two simultaneous calls this will block briefly for the second call,
-- until the first commits when it sees row count of 1
COMMIT;
-- Other sessions now see process ID and start time
-- Process Runs here, taking a couple of hours.
UPDATE institutions
SET process_acct_id = null,
process_finish = CURRENT_TIMESTAMP
WHERE institution_code = '15';
COMMIT;