【问题标题】:Oracle SQL - Make a transaction atomicOracle SQL - 使事务原子化
【发布时间】:2011-10-26 13:59:23
【问题描述】:

我有一个过程是这样的:

create or replace
PROCEDURE NEWJOBIDPROC (JOB_ID OUT NUMBER )
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
-- PROCEDURE TO RETRIEVE THE JOB ID
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
IS
BEGIN
  -- select the job_id
    SELECT VALUE+1 INTO JOB_ID FROM JOB_TABLE WHERE ID = 50;
  -- update table JOB_TABLE with the latest job id
    UPDATE JOB_TABLE SET VALUE = JOB_ID WHERE ID = 50;

END;

现在我的问题如下。

假设我同时多次调用此过程。 在我们的示例中,让我们同时调用同一个过程。 当他们两个都运行 select 语句时,他们会收到一些值 - 让它成为 200。

现在,他们都将更新 job_table,使用相同的值 200 - 这不是我想要的。我不想重复。

那么,如何将整个代码标记为原子?我希望选择和更新同时运行并且是线程安全的。我希望将两个语句一起标记为原子。

【问题讨论】:

标签: oracle stored-procedures plsql


【解决方案1】:

有关更多信息,请参阅@derobert 提供的链接,但对于您的特定示例,您可以这样做:

UPDATE JOB_TABLE SET VALUE = VALUE +1 WHERE ID = 50
 RETURNING VALUE INTO JOB_ID;

但是 - 你没有考虑过使用序列吗?

【讨论】:

【解决方案2】:

如果您打算以后更新该行,则应锁定该行。第二个事务将被阻止并等待您完成事务(默认情况),或者如果它尝试锁定指定了 NOWAIT 的行,则会收到错误。

BEGIN
  -- select the job_id and LOCK the row so that noone else can modify it
    SELECT VALUE+1 INTO JOB_ID FROM JOB_TABLE WHERE ID = 50 FOR UPDATE NOWAIT;
  -- update table JOB_TABLE with the latest job id
    UPDATE JOB_TABLE SET VALUE = JOB_ID WHERE ID = 50;    
END;

在任何一种情况下,锁定行都会阻止您描述的 "lost update" 行为。

【讨论】:

  • 我应该在SELECT语句之前锁定表并在UPDATE之后解锁它吗?
  • 您将只锁定您打算更新的行(而不是表本身)。顺便说一句,您的更新已经在后台执行此操作(在您描述的测试用例中,第二次更新将等到第一个事务结束)。在事务结束(提交或回滚)时自动释放锁。
  • 我希望这两个语句作为一个事务执行。我希望如果对这个过程的两次调用同时发生,那么表将使用两个不同的值进行更新。我的更新不会在幕后进行。
【解决方案3】:

虽然序列可能是解决您的问题的最佳解决方案,但我会使用乐观锁定来解决这类问题。 当您在 JOB_TABLE 中添加时间戳列时,您可以在选择查询中获取时间戳,并将其作为约束添加到更新查询的 where 子句中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-26
    • 2018-12-09
    • 1970-01-01
    • 2016-06-25
    • 2017-05-30
    • 2019-08-05
    相关资源
    最近更新 更多