需要在事务之外看到更新(同步问题)

时间:2015-05-18 15:55:11

标签: sql oracle transactions

我正在使用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。我怎么写它以便它们可以?我是否在自治事务中运行第一个更新?或者有更好的方法吗?

2 个答案:

答案 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;