Oracle SQL - 使事务原子化
Posted
技术标签:
【中文标题】Oracle SQL - 使事务原子化【英文标题】:Oracle SQL - Make a transaction atomic 【发布时间】: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 - 这不是我想要的。我不想重复。
那么,如何将整个代码标记为原子?我希望选择和更新同时运行并且是线程安全的。我希望将两个语句一起标记为原子。
【问题讨论】:
download.oracle.com/docs/cd/B14117_01/server.101/b10743/… Oracle SQL: How to read-and-increment a field的可能重复 【参考方案1】:有关更多信息,请参阅@derobert 提供的链接,但对于您的特定示例,您可以这样做:
UPDATE JOB_TABLE SET VALUE = VALUE +1 WHERE ID = 50
RETURNING VALUE INTO JOB_ID;
但是 - 你没有考虑过使用序列吗?
【讨论】:
序列是专门为此用例设计的。 你能给我一个有用的链接来使用序列吗?这对我的案子有什么好处? 查看文档:download.oracle.com/docs/cd/E11882_01/server.112/e26088/…,然后决定它是否对您的情况有用。【参考方案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 子句中。
【讨论】:
以上是关于Oracle SQL - 使事务原子化的主要内容,如果未能解决你的问题,请参考以下文章