使用ORA_ROWSCN的乐观锁定

      由于在校验和的乐观锁定方法中,计算散列或校验和是一个CPU密集型操作(相当占用CPU),其计算代价很昂贵。不过,如果从“网络友好性”角度看,这种方法会比较好,因为只需在网络上传输相当小的散列值。而使用ORA_ROWSCN的乐观锁定也拥有它的优点,同时计算时不是CPU密集的(不会过多占用CPU)。所以就不介绍检验和的乐观锁定方法了!

      从Oracle 10g Release 1开始,你还可以使用内置的ORA_ROWSCN函数。它的工作与前面所述的版本列技术很相似,但是可以由Oracle自动执行,而不需要在表中增加额外的列,也不需要额外的更新/维护代码来更新这个值。

      ORA_ROWSCN建立在内部Oracle系统时钟(SCN)基础上。在Oracle中,每次提交时,SCN都会推进(其他情况也可能导致SCN推进,要注意,SCN只会推进,绝对不会后退)。这个概念与前面在获取数据时得到ORA_ROWSCN的方法是一样的,更新数据时要验证SCN未修改过。之所以我会强调这一点(而不是草草带过),原因是除非你创建表时支持在行级维护ORA_ROWSCN,否则Oracle会在块级维护。也就是说,默认情况下,一个块上的多行会共享相同的ORA_ROWSCN值。如果更新一个块上的某一行,而且这个块上还有另外50行,那么这些行的ORA_ROWSCN也会推进。这往往会导致许多假警报,你认为某一行已经修改,但实际上它并没有改动。因此,需要注意这一点,并了解如何改变这种行为。

我们想查看这种行为,然后进行修改,为此还要使用前面的小表DEPT:
create table dept
2 (deptno, dname, loc, data,
3 constraint dept_pk primary key(deptno)
4 )
5 as
6 select deptno, dname, loc, rpad('*',3500,'*')
7 from scott.dept;
Table created.
现在可以观察到每一行分别在哪个块上(在这种情况下,可以假设它们都在同一个文件中,所以如果块号相同,就说明它们在同一个块上)。我使用的块大小是8 KB,一行的宽度大约3 550字节,所以我预料这个例子中每块上有两行:
select deptno, dname,
2 dbms_rowid.rowid_block_number(rowid) blockno,
3 ora_rowscn
4 from dept;
DEPTNO DNAME BLOCKNO ORA_ROWSCN
---------- -------------- ---------- ----------
10 ACCOUNTING 20972 34676029
20 RESEARCH 20972 34676029
30 SALES 20973 34676029
40 OPERATIONS 20973 34676029
不错,我们观察的结果也是这样,每块有两行。所以,下面来更新块20972上DEPTNO = 10的那一行:
update dept
2 set dname = lower(dname)
3 where deptno = 10;
1 row updated.
commit;
Commit complete.
接下来观察到,ORA_ROWSCN的结果在块级维护。我们只修改了一行,也只提交了这一行的修改,但是块20972上两行的ORA_ROWSCN值都推进了:

  select deptno, dname,
2 dbms_rowid.rowid_block_number(rowid) blockno,
3 ora_rowscn
4 from dept;
DEPTNO DNAME BLOCKNO ORA_ROWSCN
---------- -------------- ---------- ----------
10 accounting 20972 34676046
20 RESEARCH 20972 34676046
30 SALES 20973 34676029
40 OPERATIONS 20973 34676029
如果有人读取DEPTNO=20这一行,看起来这一行已经修改了,但实际上并非如此。块20973上的行是“安全”的,我们没有修改这些行,所以它们没有推进。不过,如果更新其中任何一行,两行都将推进。所以现在的问题是:如何修改这种默认行为。遗憾的是,我们必须启用ROWDEPENDENCIES再重新创建这个段。
Oracle9i为数据库增加了行依赖性跟踪,可以支持推进复制,以便更好地并行传播修改。在Oracle 10g之前,这个特性只能在复制环境中使用;但是从Oracle 10g开始,还可以利用这个特性用ORA_ROWSCN来实现一种有效的乐观锁定技术。它会为每行增加6字节的开销(所以与自己增加版本列的方法(即DIY版本列方法)相比,并不会节省空间),而实际上,也正是因为这个原因,所以需要重新创建表,而不只是简单地ALTER TABLE:必须修改物理块结构来适应这个特性。
下面重新建立我们的表,启用ROWDEPENDENCIES。可以使用DBMS_REDEFINITION中(Oracle提供的另一个包)的在线重建功能来执行,但是对于一个这么小的任务,我们还是从头开始更好一些:
drop table dept;
Table dropped.
create table dept
2 (deptno, dname, loc, data,
3 constraint dept_pk primary key(deptno)
4 )
5 ROWDEPENDENCIES
6 as
7 select deptno, dname, loc, rpad('*',3500,'*')
8 from scott.dept;
Table created.
select deptno, dname,
2 dbms_rowid.rowid_block_number(rowid) blockno,
3 ora_rowscn
4 from dept;
DEPTNO DNAME BLOCKNO ORA_ROWSCN
---------- -------------- ---------- ----------
10 ACCOUNTING 21020 34676364
20 RESEARCH 21020 34676364
30 SALES 21021 34676364
40 OPERATIONS 21021 34676364
又回到前面:两个块上有4行,它们都有相同的ORA_ROWSCN值。现在,更新DEPTNO=10的那一行时:
update dept
2 set dname = lower(dname)
3 where deptno = 10;
1 row updated.
commit;
Commit complete.
查询DEPT表时应该能观察到以下结果:
select deptno, dname,
2 dbms_rowid.rowid_block_number(rowid) blockno,
3 ora_rowscn
4 from dept;
DEPTNO DNAME BLOCKNO ORA_ROWSCN
---------- -------------- ---------- ----------
10 accounting 21020 34676381
20 RESEARCH 21020 34676364
30 SALES 21021 34676364
40 OPERATIONS 21021 34676364
此时,只有DEPTNO = 10这一行的ORA_ROWSCN改变,这正是我们所希望的。现在可以依靠ORA_ROWSCN来为我们检测行级修改了。

 

你可能感兴趣的:(数据结构,oracle)