oracle锁简述

不同的数据库它的锁定机制是不同的,即使表面上看是一样的(都是行级锁),但是他们的实现方法也是完全不同的。

一、综述

在oracle中,你会了解到:

事务是每个数据库的核心,它们是“好东西” 。

应该延迟到适当的时刻才提交。不要太快提交,以避免对系统带来压力。这是因为,如果事务很长或很大,一般不会对系统有压力。相应的原则是:在必要时才提交,但是此前不要提交。事务的大小只应该根据业务逻辑来定。

只要需要,就应该尽可能长时间地保持对数据所加的锁。这些锁是你能利用的工具,而不是让你退避三舍的东西。锁不是稀有资源。恰恰相反,只要需要,你就应该长期地保持数据上的锁。锁可能并不稀少,而且它们可以防止其他会话修改信息。

在oracle中,行级锁没有相关的开销,根本没有。不论你是有1个行锁,还是1000000个行锁,专用于锁定这个信息的“资源”数都是一样的。当然,与修改1行相比,修改1000000行要做的工作肯定多得多,但是对1000000行锁定所需的资源数与对1行锁定所需的资源数完全相同,这是一个固定的常量。

不要以为锁升级“对系统更好”(例如,使用表锁而不是行锁)。在oracle中,锁升级(lock escalate)对系统没有任何好处,不会节省任何资源。也许有时会使用表锁,如批处理中,此时你很清楚会更新整个表,而且不希望其他会话锁定表中的行。但是使用表锁绝对不是为了避免分配行锁,想以此来方便系统。

二、悲观锁和乐观锁

1、悲观锁(pessimistic locking)

在试图更新之前我们就把行锁住了,因为我们很悲观,对于这一行能不能保持未改变很是怀疑。

如何实现悲观锁:加for update nowait

保证最初读出数据之后,在我们更新之前数据没有改变。

2、乐观锁(optimistic locking)

把所有锁定都延迟到即将执行更新之前才做。
可以在update中同时保留新值和旧值,例如
update emp set sal = 2000 where empno = 100 and sal = 1000;
乐观锁如果update返回0行,说明数据在查询之后被修改了。

如何实现乐观锁:
1)在数据库表中增加一列(时间戳),timestamp类型。
update时同时增加时间戳等于旧时间的where条件。

2)使用校验和
校验和就是计算一个散列值。

单向散列函数取一个变长输入串(即数据),并把它转换为一个定长的输出串(通常更小),这个输出称为散列值(hash value)。散列值充当输入数据的一个惟一标识符(就像指纹一样)。可以使用散列值来验证数据是否被修改。

需要注意,单向散列函数只能在一个方向上应用。从输入数据计算散列值很容易,但是要生成能散 列为某个特定值的数据却很难。

使用DBMS_CRYPTO.HASH函数计算消息摘要。

3)使用ora_rowscn
ora_rowscn建立在内部oracle系统时钟(SCN)基础上,只会推进不会后退。

除非创建表时支持在行级维护ora_rowscn,oracle默认在块级维护ora_rowscn,一个块上的多行会共享相同的ora_rowscn值。

创建表时,使用rowdependencies可以使ora_rowscn在行级维护。

建议增加时间戳字段,或使用行级的ora_rowscn(开销是每行记录多6个字节存储空间)。尽量不使用校验和,因为计算校验值要花费大量CPU时间。

三、阻塞
如果一个会话持有某个资源的锁,而另一个会话在请求这个资源,就会出现阻塞(blocking)。

数据库中有5条常见的DML语句可能会阻塞,具体是:insert、update、delete、merge和select for update。对于一个阻塞的select for update,解决方案很简单:只需增加nowait子句,它就不会阻塞了。

1、阻塞的insert
insert阻塞的情况不多见。最常见的情况是,你有一个带主键的表,或者表上有惟一的约束,但有两个会话试图用同样的值插入一行。如果是这样,其中一个会话就会阻塞,直到另一个会话提交或者回滚为止;如果另一个会话提交,那么阻塞的会话会收到一个错误,指出存在一个重复值;倘若另一个会话回滚,在这种情况下,阻塞的会话则会成功。

如果应用允许最终用户生成主键 / 惟一列值,往往就会发生insert阻塞。为避免这种情况,最容易的做法是使用一个序列来生成主键 / 惟一列值。序列(sequence)设计为一种高度并发的方法,用在多用户环境中生成惟一键。

或者将唯一键设置为字段的组合是唯一的,降低应用生成唯一键重复的概率。

2、阻塞的merge、update和delete
使用悲观锁和乐观锁解决。

四、死锁
如果你有两个会话,每个会话都持有另一个会话想要的资源,都在等待对方释放锁,此时就会出现死锁(deadlock)。

oracle中导致死锁的主要原因是,有一个父表和子表,子表的外键未加索引造成的。

如果更新父表的主键或者删除父表中的一行,由于外键上没有索引,子表全表会被锁住,大量操作子表的会话会被阻塞,如果这些会话中拥有父表中资源的锁的话,极有可能造成死锁。

五、锁类型
1、TX锁(事务锁)
事务发起第一个修改时会得到TX锁(事务锁),而且会一直持有这个锁,直至事务执行提交(commit)或回滚(rollback)。

TX锁作用在数据被修改的块上。

2、TM锁
TM锁(TM lock)用于确保在修改表的内容时,表的结构不会改变。

TM锁作用在表上。

3、DDL锁
在DDL语句修改数据库对象时,会自动为对象加DDL锁(DDL Lock),从而保护这些对象不会被其他会话所修改。

DDL锁作用在对象上。