全面解析oracle中的锁机制2

默认情况下
T@ora>create table t1 as select * from t ;

Table created.

Elapsed: 00:00:00.07
T@ora>select rowid,ora_rowscn,a from t1;

ROWID                         ORA_ROWSCN          A
------------------                      ----------             ----------
AAARN5AAGAAAXHcAAA   45387504          1
AAARN5AAGAAAXHcAAB   45387504          2
AAARN5AAGAAAXHcAAC   45387504          3
AAARN5AAGAAAXHcAAD   45387504          4
AAARN5AAGAAAXHcAAE   45387504          5
AAARN5AAGAAAXHcAAF   45387504          6
AAARN5AAGAAAXHcAAG   45387504          7
AAARN5AAGAAAXHcAAH   45387504          8
AAARN5AAGAAAXHcAAI   45387504          9
AAARN5AAGAAAXHcAAJ   45387504         10

10 rows selected.

Elapsed: 00:00:00.20
T@ora>update t1 set a = 0 where a <3;

2 rows updated.

Elapsed: 00:00:00.01
T@ora>commit;

Commit complete.

Elapsed: 00:00:00.00
T@ora>select rowid,ora_rowscn,a from t1;

ROWID                           ORA_ROWSCN          A
------------------                          ----------          ----------
AAARN5AAGAAAXHcAAA   45387522          0
AAARN5AAGAAAXHcAAB   45387522          0
AAARN5AAGAAAXHcAAC   45387522          3 《----这里开始后面的记录都没做修改,但是ora_rowscn却改变了
AAARN5AAGAAAXHcAAD   45387522          4
AAARN5AAGAAAXHcAAE   45387522          5
AAARN5AAGAAAXHcAAF   45387522          6
AAARN5AAGAAAXHcAAG   45387522          7
AAARN5AAGAAAXHcAAH   45387522          8
AAARN5AAGAAAXHcAAI   45387522          9
AAARN5AAGAAAXHcAAJ   45387522         10

10 rows selected.
增加ROWDEPENDENCIES 
T@ora>create table t2 ROWDEPENDENCIES  as select * from t ;

Table created.

Elapsed: 00:00:00.06
T@ora>select rowid,ora_rowscn,a from t2;

ROWID                              ORA_ROWSCN          A
------------------                      ----------               ----------
AAARN6AAGAAAXHkAAA   45387561          1
AAARN6AAGAAAXHkAAB   45387561          2
AAARN6AAGAAAXHkAAC   45387561          3
AAARN6AAGAAAXHkAAD   45387561          4
AAARN6AAGAAAXHkAAE   45387561          5
AAARN6AAGAAAXHkAAF   45387561          6
AAARN6AAGAAAXHkAAG   45387561          7
AAARN6AAGAAAXHkAAH   45387561          8
AAARN6AAGAAAXHkAAI   45387561          9
AAARN6AAGAAAXHkAAJ   45387561         10

10 rows selected.

Elapsed: 00:00:00.03
T@ora>update t2 set a = 0 where a <3;

2 rows updated.

Elapsed: 00:00:00.01
T@ora>commit;

Commit complete.

Elapsed: 00:00:00.00
T@ora>select rowid,ora_rowscn,a from t2;

ROWID                          ORA_ROWSCN          A
------------------                         ----------         ----------
AAARN6AAGAAAXHkAAA   45387578          0
AAARN6AAGAAAXHkAAB   45387578          0
AAARN6AAGAAAXHkAAC   45387561          3 《--没有修改的scn就不会改变
AAARN6AAGAAAXHkAAD   45387561          4
AAARN6AAGAAAXHkAAE   45387561          5
AAARN6AAGAAAXHkAAF   45387561          6
AAARN6AAGAAAXHkAAG   45387561          7
AAARN6AAGAAAXHkAAH   45387561          8
AAARN6AAGAAAXHkAAI   45387561          9
AAARN6AAGAAAXHkAAJ   45387561         10

存储空间的影响,dump出来就可以看到2个存储结构不一样
普通表
tab 0, row 0, @0x1f1e
tl: 8 fb: --H-FL-- lb: 0x2  cc: 2
col  0: [ 1]  80
col  1: [ 2]  c1 02


2个number类型
tab 0, row 0, @0x1edc
tl: 14 fb: --H-FL-- lb: 0x2  cc: 2
dscn 0x0000.02b48f29
col  0: [ 1]  80
col  1: [ 2]  c1 02

多出6个字节记录dscn。


将SCN 转换为墙上时钟时间:
使用透明的ORA_ROWSCN 列还有一个好处:可以把SCN 转换为近似的墙上时钟时间(有+/–3 秒的偏差),从而发现行最后一次修改发生在什么时间。例如,可以执行以下查询:


ops$tkyte@ORA10G> select deptno, ora_rowscn, scn_to_timestamp(ora_rowscn) ts2 from dept;


DEPTNO ORA_ROWSCN          TS
----------     ----------                -------------------------------
10            34676381         25-APR-05 02.37.04.000000000 PM

20            34676364         25-APR-05 02.34.42.000000000 PM
30           34676364          25-APR-05 02.34.42.000000000 PM
40           34676364          25-APR-05 02.34.42.000000000 PM


在此可以看到,在表的最初创建和更新DEPTNO = 10 行之间,我等了大约3 分钟。不过,从SCN 到墙上时钟时间的这种转换有一些限制:数据库的正常运行时间只有5 天左右。例如,如果查看一个“旧”表,查找其中最旧ORA_ROWSCN(注意,在此我作为SCOTT 登录;没有使用前面的新表):


scott@ORA10G> select min(ora_rowscn) from dept;


MIN(ORA_ROWSCN)
---------------
364937


如果我试图把这个SCN 转换为一个时间戳,可能看到以下结果(取决于DEPT 表有多旧!):


scott@ORA10G> select scn_to_timestamp(min(ora_rowscn)) from dept;


select scn_to_timestamp(min(ora_rowscn)) from dept
*
ERROR at line 1:
ORA-08181: specified number is not a valid system change number
ORA-06512: at "SYS.SCN_TO_TIMESTAMP", line 1
ORA-06512: at line 1


所以从长远看不能依赖这种转换。

3、乐观悲观锁总结:

到底是用乐观锁呢还是悲观锁呢?现实生产环境中,使用乐观锁的机会远多于悲观锁,最大的原因就是悲观锁需要与数据库建立一个连接,这个连接会耗费很多的资源,特别是在用户数超过一百以上,每个用户有一个连接,这样是不现实的。

一般用乐观锁定的版本戳,然后再加一个时间戳,这样一来,就可以查询到每一行最后一次修改的时间啦,真是省劲快捷,hash方法那个如果碰到LONG,LONG RAW,CLOB,BLOB这等大数据就死翘翘了。。

4、阻塞和死锁:

数据库中有5条常见的语句会发生阻塞,他们分别是:insert,update,delete,merge,select for update

对于一个阻塞的SELECT FOR UPDATE,解决方案很简单:只需增加NOWAIT 子句,它就不会阻塞了。这样一来, 你的应用会向最终用户报告,这一行已经锁定。另外4 条DML 语句才有意思。我们会分别分析这些DML 语句,看看它们为什么不应阻塞,如果真的阻塞了又该如何修正。

1、阻塞的insert:

INSERT 阻塞的情况不多见。最常见的情况是,你有一个带主键的表,或者表上有惟一的约束,但有两个会话试图用同样的值插入一行。如果是这样,其中一个会话就会阻塞, 直到另一个会话提交或者回滚为止:如果另一个会话提交,那么阻塞的会话会收到一个错误,指出存在一个重复值;倘若另一个会话回滚,在这种情况下,阻塞的会话则会成功。还有一种情况,可能多个表通过引用完整性约束相互链接。对子表的插入可能会阻塞,因为它所依赖的父表正在创建或删除。如果应用允许最终用户生成主键/惟一列值,往往就会发生INSERT 阻塞。为避免这种情况,最容易的做法是使用一个序列来生成主键/惟一列值。序列(sequence)设计为一种高度并发的方法,用在多用户环境中生成惟一键。如果无法使用序列,那你可以使用以下技术,也就是使用手工锁来避免这个问题,这里的手工锁通过内置的DBMS_LOCK 包来实现。

当然,如果表的主键是一个INTEGER,而你不希望这个主键超过1 000 000 000,那么可以跳过散列,直接使用这个数作为锁ID。要适当地设置散列表的大小(在这个例子中,散列表的大小是1 024),以避免因为不同的串散列为同一个数(这称为散列冲突)而人工地导致资源忙消息。散列表的大小与特定的应用(数据)有关,并发插入的数量也会影响散列表的大小。最后,还要记住,尽管Oracle 有无限多个行级锁,但是enqueue 锁(这是一种队列锁)的个数则是有限的。如果在会话中插入大量行,而没有提交,可能就会发现创建了太多的enqueue 队列锁,而耗尽了系统的队列资源(超出了ENQUEUE_RESOURCES 系统参数设置的最大值),因为每行都会创建另一个enqueue 锁。如果确实发生了这种情况, 就需要增大ENQUEUE_RESOURCES 参数的值。还可以向触发器增加一个标志,允许打开或关闭这种检查。例如,如果我准备插入数百条或数千条记录,可能就不希望启用这个检查。

2、阻塞的update、delete和merge:

select for updatre nowait 能够避免丢失更新的问题。他能验证你自从获取数据到执行修改这期间没有别的事务来修改这个数据库,锁住这一行,防止update和delect阻塞。不论是乐观锁还是悲观锁,都会用这一句实现这些功能,只是悲观锁会在你获取这一行之后就执行了锁定,而乐观锁是在即将提交的时候才执行的。

3、死锁:

一般来看死锁的出现常见原因有两个:外键未加索引 表上的位图索引遭到并发更新

在以下情况,oracle修改父表后会对字表加一个索引:1、果更新了父表的主键,因为外键上没有索引,所以子表上会被锁住;2、删除了父表的一行,子表也会被锁住。

以上这种锁,只有在执行对应的DML语句是才会发生,而不是整个事务期间都会锁住字表,这样可以减少死锁的可能性,但是即使这样,死锁还是可以发生的。

删除父表中的一行可能导致子表被锁住,由此产生的问题更多。我已经说过,如果删除父表P 中的一行,那么在DML 操作期间,子表C 就会锁定,这样能避免事务期间对C 执行其他更新(当然,这有一个前提,即没有人在修改C;如果确实已经有人在修改C,删除会等待)。此时就会出现阻塞和死锁问题。通过锁定整个表C,数据库的并发性就会大幅下降,以至于没有人能够修改C 中的任何内容。另外,出现死锁的可能性则增加了,因为我的会话现在“拥有”大量数据,直到提交时才会交出。其他会话因为C 而阻塞的可能性也更大;只要会话试图修改C 就会被阻塞。因此,我开始注意到,数据库中大量会话被阻塞,这些会话持有另外一些资源的锁。实际上,如果其中任何阻塞的会话锁住了我的会话需要的资源,就会出现一个死锁。在这种情况下,造成死锁的原因是:我的会话不允许别人访问超出其所需的更多资源(在这里就是一个表中的所有行)。

你可能感兴趣的:(全面解析oracle中的锁机制2)