Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1118035
  • 博文数量: 231
  • 博客积分: 2500
  • 博客等级: 少校
  • 技术积分: 2662
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-03 16:35
个人简介

学无止境

文章分类

全部博文(231)

文章存档

2014年(7)

2013年(103)

2011年(11)

2010年(53)

2009年(57)

分类: Oracle

2010-09-26 15:36:47

问题描述
=============
从ORA_ROWSCN的伪列来看,数据库并发控制和乐观锁似乎并没有按照想象的工作。
 
变化
=============
所有表都有一个ORA_ROWSCN伪劣,它既不固定,也不存在于外部。它表示某行最近的SCN,也就是说是该行最后的COMMIT操作。例如:
 
SELECT ora_rowscn, last_name, salary
  FROM employees
  WHERE employee_id = 7788;
ORA_ROWSCN    NAME    SALARY
----------    ----    ------
    202553    Fudd      3000
 
取得该行最后的COMMIT操作,大约在SCN 202553。使用SCN_TO_TIMESTAMP函数可以将SCN转换成相应的TIMESTAMP值。
ORA_ROWSCN是一个保守的最后提交时间的上限值,实际commit的SCN可能更早。ORA_ROWSCN对于行依赖表(使用CREATE TABLE的ROWDEPENDENCIES子句)更精确(接近实际commit的SCN)。
应用程序检查到记录行的响应ORA_ROWSCN为202553,一会儿后,应用程序需要更新该行,但只有该行没有改变过,操作使用条件仍然让ORA_ROWSCN为202553。如下的语句:
UPDATE employees
  SET salary = salary + 100
  WHERE employee_id = 7788
  AND ora_rowscn = 202553;
0 rows updated.
 
可见,在该条件下,更新语句失败,因为ORA_ROWSCN已经不再是202553了。因此,用户或另一个应用想要改变行以及进行COMMIT操作,需要比记录的ORA_ROWSCN更新。
应用程序再次查询新数据行和ORA_ROWSCN,假设ORA_ROWSCN现在为415639。应用尝试再次带条件更新行,使用新的ORA_ROWSCN。这次更行成功了,并且commit,过程如下:
SQL> UPDATE employees SET salary = salary + 100
     WHERE empno = 7788 AND ora_rowscn = 415639;
 
1 row updated.
 
SQL> COMMIT;
 
Commit complete.
 
SQL> SELECT ora_rowscn, name, salary FROM employees WHERE empno = 7788;
 
ORA_ROWSCN    NAME    SALARY
----------    ----    ------
    465461    Fudd      3100
 
新的COMMIT响应的SCN为465461。
除了在UPDATE中使用ORA_ROWSCN之外,还可以在DELETE的WHERE子句中或者闪回查询的AS OF子句中使用。

原因
==================
如果有2个session基于ORA_ROWSCN更新同一行,可能会造成该问题。第2个hang住的session可能无法完成,因为行的scn在第1个session提交之后改变了。
下面这个例子说明这一行为:

Session 1:
**********
-- 在本例中ROWDEPENDENCIES子句存在与否对下面的过程没有影响,因为表只有1条记录。
.
SQL> create table test (a number,b number)  ROWDEPENDENCIES ;
Table created.
.
SQL>  insert into test values(1,1);
1 row created.
.
SQL> commit;
Commit complete.
.
SQL> select a,b,ora_rowscn from test where a=1;
         A          B ORA_ROWSCN
---------- ---------- ----------
         1          1     535526
.
SQL> update test set a=9,b=9 where ORA_ROWSCN=535526;
1 row updated.
.
SQL> select a,b,ora_rowscn from test ;
         A          B ORA_ROWSCN
---------- ---------- ----------
         9          9
.
Session 2:
**********
SQL> update test set a=8,b=8 where ORA_ROWSCN=535526;
.
该语句被hang住,因为session 1还没有提交。
.
Session 1:
**********
SQL> commit;
Commit complete.
.
SQL> select a,b,ora_rowscn from test ;
         A          B ORA_ROWSCN
---------- ---------- ----------
         9          9     535547
.
Session 2:
**********
刚才被hang住的session 2的update语句现在执行了,更新了如下一条语句:
.
--(hang住的语句执行成功了)
.
SQL> update test set a=8,b=8 where ORA_ROWSCN=535526;
1 row updated.
.
SQL> select a,b,ora_rowscn from test ;
         A          B ORA_ROWSCN
---------- ---------- ----------
         8          8
.
SQL> commit;
Commit complete.
.
SQL> select a,b,ora_rowscn from test ;
         A          B ORA_ROWSCN
---------- ---------- ----------
         8          8     535556
 
解决方案
==================
如果想要该表的指定行只在第1个session中有效,之后的尝试都视为NOOP(无效操作),那么最好通过select-for-update + update的方式进行,例如:
session-1: select * from t where rowid = and ora_rowscn = for update;
session-1: update t set i = where rowid = and ora_rowscn = ;
session-2: select * from t where rowid = and ora_rowscn = for update;
session-1: commit;
session-2: update t set i = where rowid = and ora_rowscn = ;
session-2的最后一句update将会失败,找不到想要的行。"rowid = "子句最好是能替换为其他定位行的条件(如其他列的值或主键等)。
例子:
SESSION 1 :
***********
SQL> create table test00 (id number, txt varchar2(30)) rowdependencies;
SQL> insert into test00 values (1, 'testing ora_rowscn');
SQL> insert into test00 values (2, 'testing ora_rowscn');
SQL> commit;
SQL> select ora_rowscn from test00 where id = 1;
SQL> select id,txt from test00 where ora_rowscn = < SCN1 > and id = 1 for update;

SQL> select * from test00 where ora_rowscn = < SCN1 > and id = 1 for update;
SQL> update test00 set txt = 'changed' where id = 1;
SESSION 2:
***********
SQL> select id,txt from test00 where ora_rowscn = and id = 1 for update;

SQL> select * from test00 where ora_rowscn = and id = 1 for update;
这里会hang住。
SESSION 1:
***********
SQL> commit;
SESSION 2:
***********
现在显示:"no rows selected"
阅读(1070) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-09-27 10:47:19

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com