Oracle 由于未写入任何行,因此采用了锁 session 1 > insert into test values (5, 456, 'active'); 1 row created.

Oracle 由于未写入任何行,因此采用了锁 session 1 > insert into test values (5, 456, 'active'); 1 row created.,oracle,transactions,isolation-level,Oracle,Transactions,Isolation Level,为相同的新AppId启动第二个事务: session 2 > update test set status = 'inactive' 2 where AppId = 456 and status = 'active'; 0 rows updated. session 1 > update test set status = 'inactive' 2 where AppId = 789 and status = 'active'; 0 r

为相同的新AppId启动第二个事务:

session 2 > update test set status = 'inactive'
  2          where AppId = 456 and status = 'active';

0 rows updated.
session 1 > update test set status = 'inactive'
  2         where AppId = 789 and status = 'active';

0 rows updated.

session 1 > insert into test values (7, 789, 'active');

1 row created.
会话2没有看到第5行,因此它不会尝试获取对该行的锁定。继续第2次会议:

session 2 > insert into test values (4, 123, 'active');

1 row created.

session 2 > commit;

Commit complete.
session 2 > insert into test values (6, 456, 'active');

1 row created.

session 2 > commit;

Commit complete.
提交会话1并查看结果:

session 1 > commit;

Commit complete.

session 1 > select * from test;

    TESTID      APPID STATUS
---------- ---------- --------
         1        123 inactive
         2        123 inactive
         3        123 inactive
         4        123 active
         5        456 active
         6        456 active

6 rows selected.
要修复此问题,请使用Patrick Marchand()中基于函数的索引:

启动新AppId的第一个事务:

session 2 > update test set status = 'inactive'
  2          where AppId = 456 and status = 'active';

0 rows updated.
session 1 > update test set status = 'inactive'
  2         where AppId = 789 and status = 'active';

0 rows updated.

session 1 > insert into test values (7, 789, 'active');

1 row created.
同样,会话1不会在更新时使用任何锁。第7行有一个写锁。启动第二个事务:

session_1> select AppId from Parent where AppId = 123 for update;

     APPID
----------
       123

session_1> update Child set Status = 'I' where AppId = 123 and Status = 'A';

1 row updated.

session_1> insert into child values (123, 'A', sysdate);

1 row created.
session_2> select AppId from Parent where AppId = 123 for update;
session_2> update Child set Status = 'I' where AppId = 123 and Status = 'A';

1 row updated.

session_2> insert into child values (123, 'A', sysdate);

1 row created.

session_2> commit;

Commit complete.

session_2> select * from Child;

     APPID S INSERTEDAT
---------- - -------------------
       123 I 2010-08-16 18:07:17
       123 I 2010-08-16 18:07:23
       123 I 2010-08-16 18:08:08
       123 A 2010-08-16 18:13:51
session 1 > update test set status = 'inactive'
  2         where AppId = 123 and status = 'active';

1 row updated.

session 1 > insert into test values (3, 123, 'active');

1 row created.
session 2 > update test set status = 'inactive'
  2         where AppId = 123 and status = 'active';
session 2 > update test set status = 'inactive'
  2         where AppId = 789 and status = 'active';

0 rows updated.

session 2 > insert into test values (8, 789, 'active');
同样,会话2看不到第7行,因此它不会试图锁定它但是insert正在尝试写入基于函数的索引上的同一插槽,并阻塞会话1持有的写入锁。会话2现在将等待会话1提交或回滚:

session 1 > commit;

Commit complete.
第二节我们看到:

insert into test values (8, 789, 'active')
*
ERROR at line 1:
ORA-00001: unique constraint (SCOTT.TEST_U) violated

此时,您的客户端可以重试整个事务。(无论是
更新
还是
插入

我认为这是不对的。所有N条记录也会使唯一索引失败…不,因为索引中的“N”条记录没有N条,它们的id(主键)总是唯一的。在11g中你可以使用虚拟列,10g中你需要基于函数的索引。它是.NET应用程序,我们有8台web服务器,我认为同步不起作用。如果我没有父表,这个选择更新技巧是否仍然有效?谢谢!如果没有父表,我不知道如何使它工作<代码>选择更新仅锁定存在的行。因此,只有一个表,您没有任何东西可以锁定,以防止多个插入在单独的事务中发生。您可以从表中测试
select*,其中AppId=:AppId for update
它可能会起作用,但比锁定表要昂贵得多,尤其是当
YourTable
增长时。您可以考虑为此目的添加父表。从AppID中选择AppID,其中AppID=123用于更新;它不会锁定整个表,对吗?@Ding:
从子表中选择AppId,其中AppId=123进行更新
不会锁定表,只有那些与where子句匹配的行。+1用于明确示例@Ding&Shannon:如果没有父表,那么可以使用dbms_lock包来模拟一个。你可以在这篇博文中看到这项技术,其中包括一条警告:。嗨,香农,我已经在我的机器上测试了select for update,它成功了。你能解释一下为什么你以前的答案是错的吗?谢谢你的详细回复@丁:因为
update
语句应该会导致会话在AppId上阻塞和序列化,除非AppId是表中新的(或者AppId没有
'active'
行。如果它是新的,则
select for update
将找不到任何要锁定的行。(没有父表,使用该AppId。)所以它没有添加任何内容。基本上,我无法复制您的senario,正如所描述的那样。+1,因为它使得绝对不可能有两个活动记录
session 1 > update test set status = 'inactive'
  2         where AppId = 123 and status = 'active';

1 row updated.

session 1 > insert into test values (3, 123, 'active');

1 row created.
session 2 > update test set status = 'inactive'
  2         where AppId = 123 and status = 'active';
session 1 > commit;

Commit complete.
1 row updated.
session 2 > select * from test;

    TESTID      APPID STATUS
---------- ---------- --------
         1        123 inactive
         2        123 inactive
         3        123 inactive
session 2 > insert into test values (4, 123, 'active');

1 row created.

session 2 > commit;

Commit complete.
    TESTID      APPID STATUS
---------- ---------- --------
         1        123 inactive
         2        123 inactive
         3        123 inactive
         4        123 active
session 1 > update test set status = 'inactive'
  2         where AppId = 456 and status = 'active';

0 rows updated.
session 1 > insert into test values (5, 456, 'active');

1 row created.
session 2 > update test set status = 'inactive'
  2          where AppId = 456 and status = 'active';

0 rows updated.
session 2 > insert into test values (6, 456, 'active');

1 row created.

session 2 > commit;

Commit complete.
session 1 > commit;

Commit complete.

session 1 > select * from test;

    TESTID      APPID STATUS
---------- ---------- --------
         1        123 inactive
         2        123 inactive
         3        123 inactive
         4        123 active
         5        456 active
         6        456 active

6 rows selected.
session 1 > delete from test where AppId = 456;

2 rows deleted.

session 1 > create unique index test_u
  2         on test (case when status = 'active' then AppId else null end);

Index created.
session 1 > update test set status = 'inactive'
  2         where AppId = 789 and status = 'active';

0 rows updated.

session 1 > insert into test values (7, 789, 'active');

1 row created.
session 2 > update test set status = 'inactive'
  2         where AppId = 789 and status = 'active';

0 rows updated.

session 2 > insert into test values (8, 789, 'active');
session 1 > commit;

Commit complete.
insert into test values (8, 789, 'active')
*
ERROR at line 1:
ORA-00001: unique constraint (SCOTT.TEST_U) violated