在Oracle中读取COMMITTED数据库隔离级别

时间:2009-09-22 21:54:50

标签: sql oracle transactions isolation-level read-committed

我正在开发一个连接到oracle的网络应用。我们在oracle中有一个表“激活”列。任何时候只有一行可以将此列设置为1。为了强制执行此操作,我们一直在Java中使用SERIALIZED隔离级别,但是我们遇到了“无法序列化事务”错误,并且无法解决原因。

我们想知道READ COMMITTED的隔离级别是否可以完成这项工作。所以我的问题是:

如果我们有一个涉及以下SQL的事务:

SELECT *
FROM MODEL;

UPDATE MODEL
SET ACTIVATED = 0;

UPDATE MODEL
SET ACTIVATED = 1
WHERE RISK_MODEL_ID = ?;

COMMIT;

鉴于这些事务中的多个事务可能同时执行,多个MODEL行是否可以将激活标志设置为1?

任何帮助都将不胜感激。

3 个答案:

答案 0 :(得分:3)

您的解决方案应该有效:您的第一次更新将锁定整个表格。如果另一个事务未完成,则更新将等待。您的第二次更新将保证只有一行的值为1,因为您正在锁定表(但它不会阻止INSERT语句)。

您还应该确保存在RISK_MODEL_ID的行(或者您在事务结束时将值为'1'的行为零)。

为了防止并发INSERT语句,你会LOCK表(在EXCLUSIVE MODE中)。

答案 1 :(得分:3)

你可以考虑使用一个独特的,基于函数的索引让Oracle处理只有一行激活标志设置为1的约束。

CREATE UNIQUE INDEX MODEL_IX ON MODEL ( DECODE(ACTIVATED, 1, 1, NULL));

这将停止将一个标志设置为1的多行,但这并不意味着总是有一行将标志设置为1.

答案 2 :(得分:2)

如果您想要确保一次只能运行一个事务,那么您可以使用FOR UPDATE语法。由于您有一行需要锁定,这是一种非常有效的方法。

declare
    cursor c is 
        select activated
        from model
        where activated = 1
        for update of activated;
    r c%rowtype;
begin
    open c;
    --  this statement will fail if another transaction is running
    fetch c in r;
    ....
    update model
    set activated = 0
    where current of c;

    update model
    set activated = 1
    where risk_model_id = ?;

    close c;

    commit;
end;
/

commit释放锁定。

默认行为是等待释放行。否则,我们可以指定NOWAIT,在这种情况下,尝试更新当前活动行的任何其他会话将立即失败,或者我们可以添加带有轮询时间的WAIT选项。 NOWAIT是选择绝对避免挂起风险的选项,它还使我们有机会通知用户其他人正在更新他们可能想知道的表格。

此方法比更新表中的所有行更具可伸缩性。使用WW显示的基于函数的索引来强制执行只有一行可以具有ACTIVATED = 1的规则。