Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2594503
  • 博文数量: 2110
  • 博客积分: 18861
  • 博客等级: 上将
  • 技术积分: 24420
  • 用 户 组: 普通用户
  • 注册时间: 2008-04-05 18:23
文章分类

全部博文(2110)

文章存档

2011年(139)

2010年(1971)

我的朋友

分类: Oracle

2010-09-30 10:57:00

 问题描述

  =============

  从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"

阅读(231) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~